BeanMakerクラスの作成
では、プログラムを作成しましょう。まず、メインプログラムとなる「BeanMaker」クラスです。ここに、クラス生成に関する機能を一通り用意します。
package jp.codezine; import java.io.*; import javassist.*; import javax.xml.parsers.*; import org.w3c.dom.*; import org.xml.sax.SAXException; public class BeanMaker { // クラスファイルの変換 // 引数に指定したクラス名のXMLファイルを読み込み、 // そのクラスファイルを書き換える public static boolean createBeanClassByXml(String s) { ClassPool pool = null; CtClass cc = null; boolean result = false; try { DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); dbfactory.setValidating(true); dbfactory.setNamespaceAware(true); DocumentBuilder builder = dbfactory.newDocumentBuilder(); File file = new File(s + ".xml"); Document doc = builder.parse(file); Element root = doc.getDocumentElement(); NodeList nodelist = root.getChildNodes(); pool = ClassPool.getDefault(); cc = pool.get(s); for (int i = 0; i < nodelist.getLength(); i++) { Node elnode = nodelist.item(i); if (!elnode.getNodeName().equals("field")) continue; Element element = (Element) nodelist.item(i); Node node = element.getElementsByTagName("name").item(0); String fldname = node.getTextContent(); System.out.println("fldname: " + fldname); Node node2 = element.getElementsByTagName("type").item(0); String fldtype = node2.getTextContent(); System.out.println("fldtype: " + fldtype); String getter = getGetter(fldname, fldtype, cc); String setter = getSetter(fldname, fldtype, cc); CtClass ftypeclass = pool.get(fldtype); if (ftypeclass != null) { CtField field = new CtField(ftypeclass, fldname, cc); field.setModifiers(Modifier.PRIVATE); try { CtField fx = cc.getDeclaredField(fldname); } catch (NotFoundException e) { cc.addField(field); System.out.println("add:" + field); if (getter != null) { cc.addMethod(CtNewMethod.make(getter, cc)); } if (setter != null) { cc.addMethod(CtNewMethod.make(setter, cc)); } } } } cc.setSuperclass(cc.getSuperclass()); cc.writeFile(); result = true; } catch (DOMException e) { e.printStackTrace(); } catch (ParserConfigurationException e) { e.printStackTrace(); } catch (SAXException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (NotFoundException e) { e.printStackTrace(); } catch (CannotCompileException e) { e.printStackTrace(); } return result; } // クラスファイルの生成 // 指定した名前のソースコードファイルを作り // コンパイルする public static boolean createClassFile(String s){ boolean result = true; FileWriter fw = null; BufferedWriter bw = null; try { String template = "public class " + s + " {}"; fw = new FileWriter(s + ".java"); bw = new BufferedWriter(fw); bw.write(template); bw.flush(); System.out.println("creaet file: " + s + ".java"); Runtime.getRuntime().exec("javac " + s + ".java"); System.out.println("compiled."); } catch (IOException e) { result = false; e.printStackTrace(); } finally { try { bw.close(); } catch (IOException e) { result = false; e.printStackTrace(); } } return result; } //Getterメソッドのテキストを生成し返す private static String getGetter(String name, String type, CtClass cc) { String result = null; String nstr = toUpLower(name); try { CtMethod m = cc.getDeclaredMethod("get" + nstr); System.out.println("get" + nstr + " is decleard."); } catch (NotFoundException e) { result = "public " + type + " get" + nstr + "(){" + "return " + name + ";" + "}"; } return result; } //Setterメソッドのテキストを生成し返す private static String getSetter(String name, String type, CtClass cc) { String result = null; String nstr = toUpLower(name); try { CtMethod m = cc.getDeclaredMethod("set" + nstr); System.out.println("set" + nstr + " is decleard."); } catch (NotFoundException e) { result = "public void" + " set" + nstr + "(" + type + " s){" + name + " = s;" + "}"; } return result; } private static String toUpLower(String s) { byte[] sarr = s.getBytes(); sarr[0] = (byte) Character.toUpperCase((char) sarr[0]); return new String(sarr); } }
Runtimeを使ったコンパイル
では、ざっとポイントを整理して説明しましょう。まず、ソースコードファイルをコンパイルする処理を行うcreateClassFileからです。ここでは、クラス名を引数に渡すと、それをもとにソースコードファイルを作成し、コンパイルします。ソースコードファイルは、単純に「中身のないpublicクラス」の定義を用意してあるだけです。
String template = "public class " + s + " {}";
たったこれだけです。そして、FileWriterを使い、これを「クラス名.java」というファイル名で書き出します。
fw = new FileWriter(s + ".java"); bw = new BufferedWriter(fw); bw.write(template); bw.flush();
これでソースコードファイルが作成できました。後は、これをコンパイルするだけです。
Runtime.getRuntime().exec("javac " + s + ".java");
Runtime.getRuntime()でRuntimeインスタンスを取得し、execでjavacを実行します。たったこれだけでソースコードをコンパイルしクラスファイルが生成できるのですから簡単です。
XMLからの読み込み
続いて、XMLファイルを読み込んでクラスファイルを改変するcreateBeanClassByXmlです。ここでは、XMLファイルを解析して必要な情報を取り出し、それらを元にフィールドとメソッドを作成しています。
ではXMLからの読み込み処理部分から見ていきましょう。XMLの解析には、まずDocumentBuilderFactoryインスタンスを作成します。インスタンス作成後、setValidatingでバリデーション(ドキュメントの妥当性チェック)機能をONにし、またsetNamespaceAwareでXML名前空間をサポートするように設定を行っておきます。
DocumentBuilderFactory dbfactory =
DocumentBuilderFactory.newInstance();
dbfactory.setValidating(true);
dbfactory.setNamespaceAware(true);
DocumentBuilderFactoryをもとに、DocumentBuilderインスタンスを作成します。これはXMLからDOM Documentのインスタンスを取得するAPIを定義するものです。このDocumentBuilderを使うことで、XML文書のDocumentインスタンスを得ることができるようになります。
DocumentBuilder builder = dbfactory.newDocumentBuilder();
必要なインスタンスが準備できたら、クラス名をもとにXMLファイルのFileインスタンスを作成し、DocumentBuilderのparseを使ってDocumentインスタンスを取得します。このDocumentから、必要なノードなどを順次取り出しながらXMLの情報を取得していくことになります。
File file = new File(s + ".xml"); Document doc = builder.parse(file);
getDocumentElementでドキュメントのElementを取得します。このElementが、いわばルートとなるノード(ここでは、fields)にアクセスするオブジェクトになります。ここからgetChildNodesですべての子ノードを管理するNodeListインスタンスを取得します。
Element root = doc.getDocumentElement(); NodeList nodelist = root.getChildNodes();
こうしてルート下の全ノードを処理する準備ができました。続いて、繰り返しを使い、NodeListから順にノードを取り出して処理をしていきます。
Node elnode = nodelist.item(i); if (!elnode.getNodeName().equals("field")) continue;
ノードがfieldでなければ次へ進みます。fieldだった場合には、そこからnameとtypeのノードを取り出し、それぞれの値を変数に取り出します。
Element element = (Element) nodelist.item(i); Node node = element.getElementsByTagName("name").item(0); String fldname = node.getTextContent(); Node node2 = element.getElementsByTagName("type").item(0); String fldtype = node2.getTextContent();
特定の名前のノードは、getElementsByTagNameによって取得することができます。またノードの値は、getTextContentによって得られます。これらを使うことで、field内のnameとtypeの値がそれぞれ用意できました。後は、getGetter/getSetterを呼び出し、アクセサ・メソッドの内容をStringで取得します。
これで、フィールドとアクセサ・メソッドを作成するためのすべての情報が用意できました。
