SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

特集記事

Javassistでクラスのバイトコードを直接書き換える

クラスのバイトコード操作によるアスペクト指向的開発

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インスタンスを作成し、DocumentBuilderparseを使って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で取得します。

 これで、フィールドとアクセサ・メソッドを作成するためのすべての情報が用意できました。

次のページ
Javassistによるクラスの操作

この記事は参考になりましたか?

特集記事連載記事一覧

もっと読む

この記事の著者

掌田 津耶乃(ショウダ ツヤノ)

三文ライター&三流プログラマ。主にビギナーに向けたプログラミング関連の執筆を中心に活動している。※現在、入門ドキュメントサイト「libro」、カード型学習サイト「CARD.tuyano.com」を公開...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/1355 2007/06/11 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー