簡単な入れ子構造を扱う #17
XMLは手段であって目的ではないと書くと他のデータ提示方式もあった方がいいのか。
school.csv
ねんちょうさん,さくら,Arlene,154 ,,Bret,170 ,,Cindy,157 ,もも,Don,161 ,,Emily,153 ねんしょうさん,ひまわり,Franklin,167 ,ちゅうりっぷ,Gert,164 ,,Harvey,169 ,,Irene,158 ,あさがお,Jose,154 ,,Katia,159 おませさん,すみれ,Lee,171 ,たんぽぽ,Maria,168
のようなCSVっぽいものからSchoolオブジェクトツリーを構築する。
空のフィールドは同上の意味とする仕様。
文字セットはプラットフォームデフォルトで固定ということに。
どうせ書くならSchoolBuilderを再定義してインタフェイス部分だけに抽象化し、
XMLSchoolBuilderとかCSVSchoolBuilderとかをその実働部隊にするのも悪くないがここではやらない。
CSVSchoolBuilder.java
import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; import java.io.LineNumberReader; import school.*; public class CSVSchoolBuilder { public School build(InputStream in) throws SchoolBuilderException { try { return parse(in); } 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 School parse(InputStream in) throws IOException { LineNumberReader r = null; try { r = new LineNumberReader(new InputStreamReader(in)); School sc = new School(); Grade ge = null; Group gp = null; while (true) { String s = r.readLine(); if (s == null) break; String[] ss = s.split(",", 5); if (ss.length != 4) throw new IOException("at line " + r.getLineNumber() + ", invalid number of fields: " + s); if (ss[0].isEmpty()) { if (ge == null) throw new IOException("at line " + r.getLineNumber() + ", void grade: " + s); } else { ge = sc.add(ss[0]); } if (ss[1].isEmpty()) { if (gp == null) throw new IOException("at line " + r.getLineNumber() + ", void group: " + s); } else { gp = ge.add(ss[1]); } if (ss[2].isEmpty() || ss[3].isEmpty()) { throw new IOException("at line " + r.getLineNumber() + ", void student: " + s); } try { gp.add(ss[2], Integer.parseInt(ss[3])); } catch (NumberFormatException e) { throw new IOException("at line " + r.getLineNumber() + ", invalid height: " + s); } } return sc; } finally { if (r != null) r.close(); } } }
本当はSchoolBuilderExceptionにメッセージ用のStringを受け入れるコンストラクタを持たせて、
CSVSchoolBuilder#parse()内のCSV形式に関わるエラーはSchoolBuilderExceptionを直接投げるようにするべきか。
他にもいろいろいい加減だ。
SchoolUser6.java
import school.*; public class SchoolUser6 { public static void main(String[] args) throws SchoolBuilderException { for (Grade ge : new CSVSchoolBuilder().build("school.csv")) { 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]"); } } } } }