簡単な入れ子構造を扱う #15
XMLで書かれたものからSchoolオブジェクトツリーを構築できるようにしてみた。
SchoolBuilder.java
import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; import school.*; public class SchoolBuilder { public School build(InputStream in) throws SchoolBuilderException { try { SAXParser p = SAXParserFactory.newInstance().newSAXParser(); Handler h = new Handler(); p.parse(in, h); return h.getSchool(); } catch (ParserConfigurationException e) { throw new SchoolBuilderException(e); } catch (SAXException e) { throw new SchoolBuilderException(e); } catch (IOException e) { throw new SchoolBuilderException(e); } } public School build(String name) throws SchoolBuilderException { BufferedInputStream in = null; try { try { in = new BufferedInputStream(new FileInputStream(name)); return build(in); } finally { if (in != null) in.close(); } } catch (IOException e) { throw new SchoolBuilderException(e); } } private class Handler extends DefaultHandler { static final int INIT= 0; static final int SCHOOL= 1; static final int GRADE= 2; static final int GROUP= 3; static final int STUDENT= 4; static final int FIN= 5; int level; School school; Grade grade; Group group; @Override public void startDocument() { level = INIT; } @Override public void endDocument() throws SAXException { if (level != FIN) throw new SAXException("unexpected end of document"); } @Override public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException { if (qName.equals("school")) { if (level != INIT) throw new SAXException("unexpected start of school"); school = new School(); level = SCHOOL; } else if (qName.equals("grade")) { if (level != SCHOOL) throw new SAXException("unexpected start of grade"); String n = attributes.getValue("name"); if (n == null) throw new SAXException("grade has no name attribute"); grade = school.add(n); level = GRADE; } else if (qName.equals("group")) { if (level != GRADE) throw new SAXException("unexpected start of group"); String n = attributes.getValue("name"); if (n == null) throw new SAXException("group has no name attribute"); group = grade.add(n); level = GROUP; } else if (qName.equals("student")) { if (level != GROUP) throw new SAXException("unexpected start of student"); String n = attributes.getValue("name"); if (n == null) throw new SAXException("student has no name attribute"); String h = attributes.getValue("height"); if (h == null) throw new SAXException("student has no height attribute"); int he; try { he = Integer.parseInt(h); } catch (NumberFormatException e) { throw new SAXException("student's height attribute isn't a number: " + h); } group.add(n, he); level = STUDENT; } else { throw new SAXException("unexpected start of " + qName); } } @Override public void endElement(String uri, String localName, String qName) throws SAXException { if (qName.equals("school")) { if (level != SCHOOL) throw new SAXException("unexpected end of school"); level = FIN; } else if (qName.equals("grade")) { if (level != GRADE) throw new SAXException("unexpected end of grade"); level = SCHOOL; } else if (qName.equals("group")) { if (level != GROUP) throw new SAXException("unexpected end of group"); level = GRADE; } else if (qName.equals("student")) { if (level != STUDENT) throw new SAXException("unexpected end of student"); level = GROUP; } else { throw new SAXException("unexpected end of " + qName); } } School getSchool() { return school; } } }
SchoolBuilderException.java
public class SchoolBuilderException extends Exception { public SchoolBuilderException(Exception e) { super(e); } }
初めはenum Levelであったわけだが内部に隠れたちょっとした状態表現ならintで十分だ。
SchoolBuilderはschoolパッケージのクラスのpublicな部分しか使用していないので、
無駄にたくさんのクラスを作る前のパッケージでも後のパッケージでも同様に使える。
SchoolUser5.java
import school.*; public class SchoolUser5 { public static void main(String[] args) throws SchoolBuilderException { for (Grade ge : new SchoolBuilder().build("school.xml")) { System.out.println("[" + ge.getName() + "]"); for (Group gp : ge) { System.out.println(" [" + gp.getName() + "]"); for (Student s : gp) { System.out.println(" [" + s.getName() + ", " + s.getHeight() + "cm]"); } } } } }