/*
 * $Header: /home/cvs/commons/dbcp-1.0/ja/src/org/apache/commons/jocl/JOCLContentHandler.java,v 1.1.1.1 2004/02/13 10:02:03 hioki Exp $
 * $Revision: 1.1.1.1 $
 * $Date: 2004/02/13 10:02:03 $
 *
 * ====================================================================
 *
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution, if
 *    any, must include the following acknowlegement:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 *
 * 4. The names "The Jakarta Project", "Commons", and "Apache Software
 *    Foundation" must not be used to endorse or promote products derived
 *    from this software without prior written permission. For written
 *    permission, please contact apache@apache.org.
 *
 * 5. Products derived from this software may not be called "Apache"
 *    nor may "Apache" appear in their names without prior written
 *    permission of the Apache Group.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 */

package org.apache.commons.jocl;

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import java.util.ArrayList;
import java.lang.reflect.InvocationTargetException;
import java.io.InputStream;
import java.io.Reader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

// to do:
//  + add support for arrays
//  + add support for strings as CDATA (makes multiline strings easier, for example)
//  ? some kind of support for invoking methods?

/**
 * Java Object Configuration Language のための {@link org.xml.sax.ContentHandler} です。
 * {@primary A {@link org.xml.sax.ContentHandler}
 * for the Java Object Configuration Language.}
 * <p>JOCL は任意の Java {@link java.lang.Object} インスタンスを生成するためのXMLの文法を提供します。
 * これは全てのXMLドキュメントタイプを定義するのではなく(ルートエレメントがありません)、XMLフラグメントの記述を
 * 各 {@link java.lang.Object <tt>Object</tt>} の生成に適用します。
 * {@primary JOCL provides an XML syntax for constructing arbitrary Java
 * {@link java.lang.Object} instances.  It does not define a full
 * XML document type (there's no root element), but rather an
 * XML fragment describing the {@link java.lang.Object <tt>Object</tt>s} to be
 * constructed.}
 * <p>JOCLフラグメント内でオブジェクトを定義する場合には <tt>object</tt> エレメントを使用します。
 * 以下に簡単な例を示します:
 * {@primary In a JOCL fragment, one may define a series of objects using
 * the <tt>object</tt> element.  A trivial example is:}
 * <pre> &lt;object class="java.util.Date"/&gt;</pre>
 * この記述は引数なしのコンストラクタで <tt>java.util.Date</tt> のインスタンスを生成します。
 * {@primary which constructs an instance of <tt>java.util.Date</tt>
 * using the no-argument constructor.}
 * <p>
 * ルートとなる <tt>&lt;object&gt;</tt> が処理された後(これは{@link XMLReader} から呼ばれる
 * {@link #endElement(java.lang.String,java.lang.String,java.lang.String)}
 * に該当します)、この生成されたオブジェクトは
 * <tt>JOCLContentHandler</tt> 内の <tt>Object</tt> のリストに追加されます。
 * {@primary After a "root-level" <tt>&lt;object&gt;</tt> element has been processed
 * (that is, once {@link #endElement(java.lang.String,java.lang.String,java.lang.String)}
 * has been invoked by the {@link XMLReader}), it will be appended to a list of <tt>Object</tt>s
 * maintained by the <tt>JOCLContentHandler</tt>.}
 * <p>
 * ({@link #size}、
 * {@link #clear}、
 * {@link #getType(int)}、
 * {@link #getValue(int)}、
 * {@link #getTypeArray}、
 * {@link #getValueArray} を参照ください。)
 * {@primary (See {@link #size},
 * {@link #clear},
 * {@link #clear(int)},
 * {@link #getType(int)},
 * {@link #getValue(int)},
 * {@link #getTypeArray},
 * and
 * {@link #getValueArray}.)}
 * <p>
 * 複数の <tt>object</tt> エレメントをフラグメント内に列挙することができます。
 * 例えば以下の JOCL フラグメントを処理し、:
 * {@primary You can list multiple <tt>object</tt> elements in a fragment.  For example,
 * after processing the JOCL fragment:}
 * <pre> &lt;object class="java.util.Date"/&gt;
 * &lt;object class="java.util.Date"/&gt;</pre>
 * その後に {@link #getTypeArray} メソッドを呼んだ場合、2つの <tt>java.util.Date</tt> インスタンスが返されます。
 * 配列内の {@link java.lang.Object <tt>Object</tt>} の順番は JOCL フラグメント内の
 *  <tt>&lt;object&gt;</tt> エレメントの記述の順番と一致します。
 * {@primary The {@link #getTypeArray} method
 * will return an composed
 * of two instances of <tt>java.util.Date</tt>.  The sequence of
 * {@link java.lang.Object <tt>Object</tt>s} in the array
 * will correspond to the sequence of <tt>&lt;object&gt;</tt> elements in the JOCL fragment.}
 * <p>
 * 上記のように子エレメントを使用しない場合、 <tt>&lt;object&gt;</tt>
 * タグは指定されたクラスの引数のないコンストラクタを呼びますが、ネストした
 * <tt>&lt;object&gt;</tt> タグを使用すればコンストラクタに引数を指定することができます。
 * 例えば、:
 * {@primary As we've seen, when used with no child-elements, the <tt>&lt;object&gt;</tt>
 * tag will cause the no-argument constructor of the specified class to be invoked.
 * It is also possible to nest <tt>&lt;object&gt;</tt> tags to provide arguments
 * for the constructor.
 * For example, the fragment:}
 * <pre> &lt;object class="mypackage.Foo"&gt;
 *   &lt;object class="mypackage.Bar"/&gt;
 * &lt;/object&gt;</pre>
 * このフラグメントは <tt>new mypackage.Foo(new mypackage.Bar())</tt> で生成された
 * <tt>mypackage.Foo</tt> インスタンスをオブジェクトリストに追加します。
 * {@primary will add an instance of <tt>mypackage.Foo</tt> to the object list, constructed via
 * <tt>new mypackage.Foo(new mypackage.Bar())</tt>.}
 * <p>
 * プリミティブ型と {@link java.lang.String <tt>String</tt>} の引数を生成する場合には上記とは別の文法を使用します。
 * 以下にいくつかの例をあげます:
 * {@primary There is a special syntax available creating primative values and arguments,
 * as well as for constructing {@link java.lang.String <tt>String</tt>}s. Some examples:}
 * <p>
 * <pre> &lt;byte value="3"/&gt
 * &lt;boolean value="false"/&gt
 * &lt;char value="c"/&gt
 * &lt;double value="3.14159"/&gt
 * &lt;float value="3.14"/&gt
 * &lt;int value="17"/&gt
 * &lt;long value="1700000"/&gt
 * &lt;short value="1"/&gt
 * &lt;string value="The quick brown fox..."/&gt;</pre>
 * <p>
 * ルートレベル(親に <tt>&lt;object&gt;</tt> を持たない位置)に上記プリミティブ型のフラグメントが合った場合には、
 * 各プリミティブ型に該当するラッパークラスが生成され、{@link java.lang.Object <tt>Object</tt>} のリストに追加されます。
 * しかし、これらのオブジェクトに対する {@link #getType type} は適切なプリミティブ型を返します。
 * <tt>&lt;object&gt;</tt> を親に持つ状態で記述された場合には指定された {@link java.lang.Object <tt>Object</tt>} 
 * のコンストラクタのプリミティブ型の引数として扱われます。例えば、: 
 * <p>
 * <pre> &lt;int value="5"/&gt;
 * &lt;int value="26"/&gt;
 * &lt;int value="100"/&gt;</pre>
 * <p>
 * の処理は3つの {@link java.lang.Integer} インスタンスを値のリストに追加し、
 * {@link java.lang.Integer#TYPE} を型のリストに追加します。
 * {@primary <p>
 * When invoked at the "root" level (that is, with no <tt>&lt;object&gt;</tt> parent),
 * this will cause the corresponding "object wrapper" to be added to the list of
 * {@link java.lang.Object <tt>Object</tt>}s.  The {@link #getType type} for these
 * objects will reflect the proper primative type, however.  When invoked with an
 * <tt>&lt;object&gt;</tt> parent, these will be treated as primitive arguments to the
 * specified {@link java.lang.Object <tt>Object</tt>}'s constructor.  For example, while:
 * <p>
 * <pre> &lt;int value="5"/&gt;
 * &lt;int value="26"/&gt;
 * &lt;int value="100"/&gt;</pre>
 * <p>
 * results in three {@link java.lang.Integer} instances being added to the
 * list of values, with types corresponding to {@link java.lang.Integer}, the fragment:
 * <p>
 * <pre> &lt;int value="5"/&gt;
 *  &lt;int value="26"/&gt;
 *  &lt;int value="100"/&gt;</pre>
 * <p>
 * results in three {@link java.lang.Integer} instances being added to the
 * list of values, with types corresponding to {@link java.lang.Integer#TYPE}.
 * <p>}
 * {@note <p>記述が重複していておかしい気がしたので構成しなおしてみました。</p>}
 * 従って、 <tt>mypackage.Foo(java.lang.Integer,java.lang.Integer,java.lang.Integer)</tt>
 * というコンストラクタを呼ぶ場合は以下のようになります:
 * {@primary Hence if you want to invoke the <tt>mypackage.Foo(java.lang.Integer,java.lang.Integer,java.lang.Integer)</tt>
 * constructor, use:}
 * <pre> &lt;object class="mypackage.Foo"/&gt;
 *   &lt;object class="java.lang.Integer"&gt;&lt;int value="5"/&gt;&lt;/object&gt;
 *   &lt;object class="java.lang.Integer"&gt;&lt;int value="26"/&gt;&lt;/object&gt;
 *   &lt;object class="java.lang.Integer"&gt;&lt;int value="100"/&gt;&lt;/object&gt;
 * &lt;/object&gt;</pre>
 * <p>
 * また、 <tt>mypackage.Foo(int,int,int)</tt>
 * というコンストラクタを呼ぶ場合は以下のようになります:
 * {@primary If you want to invoke the <tt>mypackage.Foo(int,int,int)</tt>
 * constructor, use:}
 * <pre> &lt;object class="mypackage.Foo"/&gt;
 *   &lt;int value="5"/&gt;
 *   &lt;int value="26"/&gt;
 *   &lt;int value="100"/&gt;
 * &lt;/object&gt;</pre>
 * <p>
 * <tt>null</tt> オブジェクトを生成したい場合には以下のようになります:
 * {@primary If you'd like to creat a <tt>null</tt> object, use:}
 * <pre> &lt;object class="mypackage.Bar" null="true"/&gt;</pre>
 * <p>
 * 以下に単純ですが、全てのパターンを網羅した例を示します:
 * {@primary Here's a simple but complete example:}
 * <pre> &lt;?xml version="1.0"?&gt;
 * &lt;arbitrary-root xmlns="http://apache.org/xml/xmlns/jakarta/commons/jocl"&gt;
 *   &lt;string value="Hello World!"/&gt;
 *   &lt;string/&gt;
 *   &lt;boolean/&gt;
 *   &lt;boolean value="true"/&gt;
 *   &lt;byte value="1"/&gt;
 *   &lt;short value="1"/&gt;
 *   &lt;int value="1"/&gt;
 *   &lt;long value="1"/&gt;
 *   &lt;float value="1.0"/&gt;
 *   &lt;double value="1.0"/&gt;
 *   &lt;object class="java.util.Date"/&gt;
 *   &lt;object class="java.util.Date"&gt;
 *    &lt;int value="1"/&gt;
 *    &lt;int value="1"/&gt;
 *    &lt;int value="1"/&gt;
 *   &lt;/object&gt;
 * &lt;/arbitrary-root&gt;</pre>
 * <p>
 * 正式な JOCL の文法を表す DTD は以下のようになります:
 * {@primary Formally, a DTD for the JOCL grammar is as follows:}
 * <p>
 * <pre>
 * &lt;!ELEMENT object (object|byte|boolean|char|double|float|int|long|short|string)*&gt;
 * &lt;!ATTLIST object
 *   class CDATA #REQUIRED
 *   null (true|false) "false"&gt;
 *
 * &lt;!ELEMENT byte EMPTY&gt;
 * &lt;!ATTLIST byte value CDATA #REQUIRED&gt;
 *
 * &lt;!ELEMENT boolean EMPTY&gt;
 * &lt;!ATTLIST boolean value (true|false) #REQUIRED&gt;
 *
 * &lt;!ELEMENT char EMPTY&gt;
 * &lt;!ATTLIST char value CDATA #REQUIRED&gt;
 *
 * &lt;!ELEMENT double EMPTY&gt;
 * &lt;!ATTLIST double value CDATA #REQUIRED&gt;
 *
 * &lt;!ELEMENT float EMPTY&gt;
 * &lt;!ATTLIST float value CDATA #REQUIRED&gt;
 *
 * &lt;!ELEMENT int EMPTY&gt;
 * &lt;!ATTLIST int value CDATA #REQUIRED&gt;
 *
 * &lt;!ELEMENT long EMPTY&gt;
 * &lt;!ATTLIST long value CDATA #REQUIRED&gt;
 *
 * &lt;!ELEMENT short EMPTY&gt;
 * &lt;!ATTLIST short value CDATA #REQUIRED&gt;
 *
 * &lt;!ELEMENT string EMPTY&gt;
 * &lt;!ATTLIST string value CDATA #REQUIRED&gt;
 * </pre>
 * <p>
 * また、このクラスは文法の一部として JOCL を使用する {@link org.xml.sax.ContentHandler}
 * の基底クラスとして使用することができます。
 * 単純にこのクラスを継承し、新たなタグを処理するために {@link #startElement}、 {@link #characters}
 * {@link #endElement} をオーバーライドし、JOCL で定義されたエレメントとデータを処理するために親クラスのメソッド
 * (例. <tt>super.<i>XXX</i></tt>) をコールして下さい。
 * {@primary This class can also be used as a base class for {@link org.xml.sax.ContentHandler}s
 * that include JOCL as part of their grammar.  Simply extend this class, and override the
 * {@link #startElement},
 * {@link #characters},
 * and {@link #endElement} methods to handle
 * your tags, and invoke the method of the parent class (i.e., <tt>super.<i>XXX</i></tt> for
 * elements and data that you don't handle.}
 * <p>
 * 簡単に {@link InputStream}、 {@link Reader}、 {@link InputSource} からオブジェクトのリストを読み込むことのできる複数の
 * static メソッドが利用可能です。
 * {@primary A number of static methods are available for simply reading a list of objects from
 * a {@link InputStream}, {@link Reader} or {@link InputSource}.}
 * <p>
 * <b>このクラスは synchronized ではない事に注意してください。</b>
 * {@primary <b>Note that this class is not synchronized.</b>}
 * <p>
 * @author Rodney Waldhoff
 * @translator 日置 聡
 * @status firstdraft
 * @update 2003/09/05
 * @version $Id: JOCLContentHandler.java,v 1.1.1.1 2004/02/13 10:02:03 hioki Exp $
 */
public class JOCLContentHandler extends DefaultHandler implements ContentHandler {

    //--- Static Methods ---------------------------------------------
    /**
     * 単純なテストメソッドです。
     * 標準入力から JOCL を読み込み、生成されたオブジェクトのリストを標準出力に書き出します。
     * ({@link XMLReader} を指定するためには <tt>org.xml.sax.driver</tt> システムプロパティを使用してください。)
     * {@primary A simple tester method.  Reads a JOCL document from standard in
     * and prints a list of the objects created to standard out.
     * (Use the <tt>org.xml.sax.driver</tt> system property to specify
     * an {@link XMLReader}.}
     */
    public static void main(String[] args) throws Exception {
        JOCLContentHandler jocl = JOCLContentHandler.parse(System.in,null);
        for(int i=0;i<jocl.size();i++) {
            System.out.println("<" + jocl.getType(i) + ">\t" + jocl.getValue(i));
        }
    }

    /**
     * <tt>org.xml.sax.driver</tt> プロパティで指定された {@link XMLReader}
     * を使用して指定されたファイルから JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified file, using the
     * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
     * property.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param f JOCL ドキュメントを含む {@link File}
     * {@primary a {@link File} containing the JOCL document}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(File f) throws SAXException, FileNotFoundException, IOException {
        return JOCLContentHandler.parse(new FileInputStream(f),null);
    }

    /**
     * <tt>org.xml.sax.driver</tt> プロパティで指定された {@link XMLReader}
     * を使用して指定された {@link Reader} から JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified {@link Reader}, using the
     * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
     * property.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param in JOCL ドキュメントを含む {@link Reader}
     * {@primary a {@link Reader} containing the JOCL document}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(Reader in) throws SAXException, IOException {
        return JOCLContentHandler.parse(new InputSource(in),null);
    }

    /**
     * <tt>org.xml.sax.driver</tt> プロパティで指定された {@link XMLReader}
     * を使用して指定された {@link InputStream} から JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified {@link InputStream}, using the
     * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
     * property.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param in JOCL ドキュメントを含む {@link InputStream}
     * {@primary a {@link InputStream} containing the JOCL document}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(InputStream in) throws SAXException, IOException {
        return JOCLContentHandler.parse(new InputSource(in),null);
    }

    /**
     * <tt>org.xml.sax.driver</tt> プロパティで指定された {@link XMLReader}
     * を使用して指定された {@link InputSource} から JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified {@link InputSource}, using thethe
     * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
     * property.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param in JOCL ドキュメントを含む {@link InputSource}
     * {@primary a {@link InputSource} containing the JOCL document}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(InputSource in) throws SAXException, IOException {
        return JOCLContentHandler.parse(in,null);
    }

    /**
     * 指定された {@link XMLReader} を使用して指定された ファイル
     * から JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified file, using the
     * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
     * property.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param f JOCL ドキュメントを含む {@link File}
     * {@primary a {@link File} containing the JOCL document}
     * @param reader ファイルをパーすする際に使用される {@link XMLReader}
     * {@primary the {@link XMLReader} to use to parse the file}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(File f, XMLReader reader) throws SAXException, FileNotFoundException, IOException {
        return JOCLContentHandler.parse(new FileInputStream(f),reader);
    }

    /**
     * 指定された {@link XMLReader} を使用して指定された {@link Reader}
     * から JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified {@link Reader}, using the specified
     * {@link XMLReader}.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param in JOCL ドキュメントを含む {@link Reader}
     * {@primary a {@link Reader} containing the JOCL document}
     * @param reader ファイルをパーすする際に使用される {@link XMLReader}
     * {@primary the {@link XMLReader} to use to parse the file}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(Reader in, XMLReader reader) throws SAXException, IOException {
        return JOCLContentHandler.parse(new InputSource(in),reader);
    }

    /**
     * 指定された {@link XMLReader} を使用して指定された {@link InputStream}
     * から JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified {@link InputStream}, using the specified
     * {@link XMLReader}.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param in JOCL ドキュメントを含む {@link InputStream}
     * {@primary a {@link InputStream} containing the JOCL document}
     * @param reader ファイルをパーすする際に使用される {@link XMLReader}
     * {@primary the {@link XMLReader} to use to parse the file}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(InputStream in, XMLReader reader) throws SAXException, IOException {
        return JOCLContentHandler.parse(new InputSource(in),reader);
    }

    /**
     * 指定された {@link XMLReader} を使用して指定された {@link InputSource}
     * から JOCL ドキュメントを読み込み、パースします。
     * 返される {@link JOCLContentHandler} はファイルに記述されたオブジェクトのリストを内部に持ちます。
     * {@primary Parses a JOCL document from the specified {@link InputSource}, using the
     * specified {@link XMLReader}.
     * The returned {@link JOCLContentHandler} will contain the
     * list of objects described by the file.}
     * @param in JOCL ドキュメントを含む {@link InputSource}
     * {@primary a {@link InputSource} containing the JOCL document}
     * @param reader ファイルをパーすする際に使用される {@link XMLReader}
     * {@primary the {@link XMLReader} to use to parse the file}
     * @return JOCL ドキュメントに記述されたオブジェクトの配列を内部に持つ {@link JOCLContentHandler}
     * {@primary a {@link JOCLContentHandler} containing the list of objects described by the JOCL document}
     */
    public static JOCLContentHandler parse(InputSource in, XMLReader reader) throws SAXException, IOException {
        JOCLContentHandler jocl = new JOCLContentHandler();
        if(null == reader) {
            reader = XMLReaderFactory.createXMLReader();
        }
        reader.setContentHandler(jocl);
        reader.parse(in);
        return jocl;
    }

    //--- Construtors ------------------------------------------------

    /**
     * {@link #JOCLContentHandler(boolean,boolean,boolean,boolean) JOCLContentHandler(true,true,true,true)} と同等です。
     * {@primary Equivalent to {@link #JOCLContentHandler(boolean,boolean,boolean,boolean) JOCLContentHandler(true,true,true,true)}.}
     */
    public JOCLContentHandler() {
        this(true,true,true,true);
    }

    /**
     * JOCLContentHandler を生成します。
     * {@primary Construct a JOCLContentHandler.}
     * @param emptyEltNS <tt>true</tt> の場合、ネームスペースのない全てのエレメントを JOCL ネームスペースに含まれるとみなします
     * {@primary when <tt>true</tt> I should assume any element with an empty namespace is within the JOCL namespace}
     * @param joclEltPrefix <tt>true</tt> の場合、<tt>jocl:</tt> のプレフィックスを持ち、ネームスペースのないエレメントを JOCL ネームスペースに含まれるとみなします
     * {@primary when <tt>true</tt> I should assume any element who's prefix is <tt>jocl:</tt> and who's namespace is empty is within the JOCL namespace}
     * @param emptyAttrNS <tt>true</tt> の場合、ネームスペースのない全ての属性を JOCL ネームスペースに含まれるとみなします
     * {@primary when <tt>true</tt> I should assume any attribute with an empty namespace is within the JOCL namespace}
     * @param joclAttrPrefix <tt>true</tt> の場合、<tt>jocl:</tt> のプレフィックスを持ち、ネームスペースのない属性を JOCL ネームスペースに含まれるとみなします
     * {@primary when <tt>true</tt> I should assume any attribute who's prefix is <tt>jocl:</tt> and who's namespace is empty is within the JOCL namespace}
     */
    public JOCLContentHandler(boolean emptyEltNS, boolean joclEltPrefix, boolean emptyAttrNS, boolean joclAttrPrefix) {
        _acceptEmptyNamespaceForElements = emptyEltNS;
        _acceptJoclPrefixForElements = joclEltPrefix;
        _acceptEmptyNamespaceForAttributes = emptyAttrNS;
        _acceptJoclPrefixForAttributes = joclAttrPrefix;
    }

    //--- Public Methods - Accessing Objects -------------------------

    /**
     * リスト内の値と型の数を返します。
     * {@primary Returns the number of values and types in my list.}
     * @return リスト内の値と型の数
     * {@primary the number of values and types in my list.}
     */
    public int size() {
        return _typeList.size();
    }

    /**
     * リスト内の全ての値と型の数を削除します。
     * {@primary Clears all the values and types in my list.}
     */
    public void clear() {
        _typeList = new ArrayList();
        _valueList = new ArrayList();
    }

    /**
     * 指定されたインデックスの値/型のペアを削除します。
     * {@primary Removes the value/type pair at the specified index.}
     */
    public void clear(int i) {
        _typeList.remove(i);
        _valueList.remove(i);
    }

    /**
     * 指定されたインデックスのオブジェクトの型を返します。
     * {@primary Returns the type of the object at the specified index.}
     */
    public Class getType(int i) {
        return(Class)(_typeList.get(i));
    }

    /**
     * 指定されたインデックスのオブジェクトの値を返します。
     * {@primary Returns the value of the object at the specified index.}
     */
    public Object getValue(int i) {
        return _valueList.get(i);
    }

    /**
     * 値のリストのシャローコピーを返します。
     * {@primary Returns a shallow copy of my list of values.}
     */
    public Object[] getValueArray() {
        return _valueList.toArray();
    }

    /**
     * 型のリストのシャローコピーを返します。
     * {@primary Returns a shallow copy of my list of types.}
     */
    public Object[] getTypeArray() {
        return _typeList.toArray();
    }

    //--- Public Methods - DocumentHandler ---------------------------

    public void startElement(String uri, String localName, String qname, Attributes attr) throws SAXException {
        try {
            if(isJoclNamespace(uri,localName,qname)) {
                if(ELT_OBJECT.equals(localName)) {
                    String cname = getAttributeValue(ATT_CLASS,attr);
                    String isnullstr = getAttributeValue(ATT_ISNULL,attr,"false");
                    boolean isnull = ("true".equalsIgnoreCase(isnullstr) || "yes".equalsIgnoreCase(isnullstr));
                    _cur = new ConstructorDetails(cname,_cur,isnull);
                } else if(ELT_BOOLEAN.equals(localName)) {
                    String valstr = getAttributeValue(ATT_VALUE,attr,"false");
                    boolean val = ("true".equalsIgnoreCase(valstr) || "yes".equalsIgnoreCase(valstr));
                    addObject(Boolean.TYPE,new Boolean(val));
                } else if(ELT_BYTE.equals(localName)) {
                    byte val = Byte.parseByte(getAttributeValue(ATT_VALUE,attr,"0"));
                    addObject(Byte.TYPE,new Byte(val));
                } else if(ELT_CHAR.equals(localName)) {
                    char val = '\u0000';
                    String valstr = getAttributeValue(ATT_VALUE,attr);
                    if(null == valstr) {
                        val = '\u0000';
                    } else if(valstr.length() > 1) {
                        throw new SAXException("if present, char value must be exactly one character long");
                    } else if(valstr.length()==1) {
                        val = valstr.charAt(0);
                    } else if(valstr.length()==0) {
                        throw new SAXException("if present, char value must be exactly one character long");
                    }
                    addObject(Character.TYPE,new Character(val));
                } else if(ELT_DOUBLE.equals(localName)) {
                    double val = Double.parseDouble(getAttributeValue(ATT_VALUE,attr,"0"));
                    addObject(Double.TYPE,new Double(val));
                } else if(ELT_FLOAT.equals(localName)) {
                    float val = Float.parseFloat(getAttributeValue(ATT_VALUE,attr,"0"));
                    addObject(Float.TYPE,new Float(val));
                } else if(ELT_INT.equals(localName)) {
                    int val = Integer.parseInt(getAttributeValue(ATT_VALUE,attr,"0"));
                    addObject(Integer.TYPE,new Integer(val));
                } else if(ELT_LONG.equals(localName)) {
                    long val = Long.parseLong(getAttributeValue(ATT_VALUE,attr,"0"));
                    addObject(Long.TYPE,new Long(val));
                } else if(ELT_SHORT.equals(localName)) {
                    short val = Short.parseShort(getAttributeValue(ATT_VALUE,attr,"0"));
                    addObject(Short.TYPE,new Short(val));
                } else if(ELT_STRING.equals(localName)) {
                    String val = getAttributeValue(ATT_VALUE,attr);
                    addObject("".getClass(),val);
                } else {
                    // unrecognized JOCL element warning?
                }
            }
        } catch(Exception e) {
            throw new SAXException(e);
        }
    }

    public void endElement(String uri, String localName, String qname) throws SAXException {
        try {
            if(isJoclNamespace(uri,localName,qname)) {
                if(ELT_OBJECT.equals(localName)) {
                    ConstructorDetails temp = _cur;
                    _cur = _cur.getParent();
                    if(null == _cur) {
                        _typeList.add(temp.getType());
                        _valueList.add(temp.createObject());
                    } else {
                        _cur.addArgument(temp.getType(),temp.createObject());
                    }
                } else if(ELT_BOOLEAN.equals(localName)) {
                    // nothing to do here
                } else if(ELT_BYTE.equals(localName)) {
                    // nothing to do here
                } else if(ELT_CHAR.equals(localName)) {
                    // nothing to do here
                } else if(ELT_DOUBLE.equals(localName)) {
                    // nothing to do here
                } else if(ELT_FLOAT.equals(localName)) {
                    // nothing to do here
                } else if(ELT_INT.equals(localName)) {
                    // nothing to do here
                } else if(ELT_LONG.equals(localName)) {
                    // nothing to do here
                } else if(ELT_SHORT.equals(localName)) {
                    // nothing to do here
                } else if(ELT_STRING.equals(localName)) {
                    // nothing to do here
                } else {
                    // unrecognized JOCL element warning?
                }
            }
        } catch(Exception e) {
            throw new SAXException(e);
        }
    }

    public void setDocumentLocator(Locator locator) {
        _locator = locator;
    }

    //--- Protected Methods ------------------------------------------

    /**
     * 指定されたエレメントを定義する属性が(現在の設定で)
     * JOCL ネームスペースに含まれる場合、 <tt>true</tt> を返します。
     * {@primary Returns <tt>true</tt> if the given attributes define an
     * element within the JOCL namespace (according to my current
     * configuration.)}
     *
     * @see #_acceptEmptyNamespaceForElements
     * @see #_acceptJoclPrefixForElements
     */
    protected boolean isJoclNamespace(String uri, String localname, String qname) {
        if(JOCL_NAMESPACE_URI.equals(uri)) {
            return true;
        } else if(_acceptEmptyNamespaceForElements && (null == uri || "".equals(uri))) {
            return true;
        } else if(_acceptJoclPrefixForElements && (null == uri || "".equals(uri)) && qname.startsWith(JOCL_PREFIX)) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * {@link #getAttributeValue(java.lang.String,org.xml.sax.Attributes,java.lang.String) <tt>getAttributeValue(localname,attr,null)</tt>} と同等です。
     * {@primary Equivalent to {@link #getAttributeValue(java.lang.String,org.xml.sax.Attributes,java.lang.String) <tt>getAttributeValue(localname,attr,null)</tt>}.}
     */
    protected String getAttributeValue(String localname, Attributes attr) {
        return getAttributeValue(localname,attr,null);
    }

    /**
     * JOCL ネームスペース内の指定された <tt><i>localname</i></tt>
     * に該当する属性の値を指定された {@link Attributes} の中から返します。
     * 該当する属性が見つからない場合、<tt><i>implied</i></tt> を返します。
     * {@primary Returns the value of attribute with the given
     * <tt><i>localname</i></tt> within the JOCL
     * namespace from the given set of {@link Attributes}.
     * If no such attribute can be found, returns
     * <tt><i>implied</i></tt>.}
     *
     * @param localname 前置修飾子を含まない検索対象となる属性名
     * {@primary the unqualified name of the attribute to look for}
     * @param attr      値を探す対象となる属性一覧
     * {@primary the Attributes in which to find the value}
     * @param implied   属性のデフォルトの値
     * {@primary the default value for the attribute}
     * @return the {@link Attributes} の中の
     *         JOCL ネームスペース内の指定された <tt><i>localname</i></tt>
     *         に該当する属性の値、
     *         該当する属性が見つからない場合、<tt><i>implied</i></tt>
     * {@primary the value of attribute with the given
     *         <tt><i>localname</i></tt> within the JOCL
     *         namespace from the given set of {@link Attributes}.
     *         If no such attribute can be found, returns
     *         <tt><i>implied</i></tt>.}
     */
    protected String getAttributeValue(String localname, Attributes attr, String implied) {
        String val = attr.getValue(JOCL_NAMESPACE_URI,localname);
        if(null == val && _acceptEmptyNamespaceForAttributes) {
            val = attr.getValue("",localname);
        }
        if(null == val && _acceptJoclPrefixForAttributes) {
            val = attr.getValue("",JOCL_PREFIX + localname);
        }
        return(null == val ? implied : val);
    }

    /**
     * 指定されたオブジェクトを型/値のリストか、現在生成中ののオブジェクトのコンストラクタの引数のいずれかに追加します。
     * {@primary Add the specified object either to my type/value list, or
     * as an argument to the object I'm currently constructing.}
     */
    protected void addObject(Class type, Object val) {
        if(null == _cur) {
            _typeList.add(type);
            _valueList.add(val);
        } else {
            _cur.addArgument(type,val);
        }
    }

    //--- Protected Attributes ---------------------------------------

    /**
     * JOCL ネームスペース URI、 <tt>http://apache.org/xml/xmlns/jakarta/commons/jocl</tt>。
     * {@primary The JOCL namespace URI, <tt>http://apache.org/xml/xmlns/jakarta/commons/jocl</tt>.}
     */
    public static final String JOCL_NAMESPACE_URI = "http://apache.org/xml/xmlns/jakarta/commons/jocl";

    /**
     * デフォルト JOCL プレフィックス、 <tt>jocl:</tt>。
     * {@primary The default JOCL prefix, <tt>jocl:</tt>.}
     */
    public static final String JOCL_PREFIX = "jocl:";

    /**
     * パース処理にて生成された型( {@link Class} )のリスト。
     * {@primary A list of the types ({@link Class}es) already created via the parse.}
     */
    protected ArrayList _typeList = new ArrayList();

    /**
     * パース処理にて生成された値( {@link Object} )のリスト。
     * {@primary A list of the values ({@link Object}s) already created via the parse.}
     */
    protected ArrayList _valueList = new ArrayList();

    /**
     * 現在処理中のオブジェクト。
     * {@primary The object I'm currently working on.}
     */
    protected ConstructorDetails _cur = null;

    /**
     * <tt>true</tt> の場合、ネームスペースURIを持たないエレメントを
     * JOCL ネームスペースの一部として扱います。
     * {@primary When <tt>true</tt>, I will treat elements with an
     * empty namespace URI as part of the JOCL namespace.}
     *
     * @see #JOCL_NAMESPACE_URI
     */
    protected boolean _acceptEmptyNamespaceForElements = true;

    /**
     * <tt>true</tt> の場合、 {@link #JOCL_PREFIX} を持つがネームスペースURIを持たないエレメントを
     * JOCL ネームスペースにマッピングします。
     * {@primary When <tt>true</tt>, I will treat elements with the
     * {@link #JOCL_PREFIX} but no namespace URI as being
     * mapped to the jocl namespace.}
     *
     * @see #JOCL_PREFIX
     * @see #JOCL_NAMESPACE_URI
     */
    protected boolean _acceptJoclPrefixForElements = true;

    /**
     * <tt>true</tt> の場合、ネームスペースURIを持たない属性を
     * JOCL ネームスペースの一部として扱います。
     * {@primary When <tt>true</tt>, I will treat attributes with an
     * empty namespace URI as part of the JOCL namespace.}
     *
     * @see #JOCL_NAMESPACE_URI
     */
    protected boolean _acceptEmptyNamespaceForAttributes = true;

    /**
     * <tt>true</tt> の場合、 {@link #JOCL_PREFIX} を持つがネームスペースURIを持たない属性を
     * JOCL ネームスペースにマッピングします。
     * {@primary When <tt>true</tt>, I will treat attributes with the
     * {@link #JOCL_PREFIX} but no namespace URI as being
     * mapped to the jocl namespace.}
     *
     * @see #JOCL_PREFIX
     * @see #JOCL_NAMESPACE_URI
     */
    protected boolean _acceptJoclPrefixForAttributes = true;

    /** 内部で使用する {@link Locator}。 {@primary My {@link Locator}.} */
    protected Locator _locator = null;

    /** "object" エレメント名。 {@primary The name of the "object" element.} */
    protected static final String ELT_OBJECT  = "object";

    /** "object" エレメントの "class" 属性名。 {@primary The name of the "object" element's "class" attribute.} */
    protected static final String ATT_CLASS   = "class";

    /** "object" エレメントの "isnull" 属性名。 {@primary The name of the "object" element's "isnull" attribute.} */
    protected static final String ATT_ISNULL  = "null";

    /** "boolean" エレメント名。 {@primary The name of the "boolean" element.} */
    protected static final String ELT_BOOLEAN = "boolean";

    /** "byte" エレメント名。 {@primary The name of the "byte" element.} */
    protected static final String ELT_BYTE    = "byte";

    /** "char" エレメント名。 {@primary The name of the "char" element.} */
    protected static final String ELT_CHAR    = "char";

    /** "double" エレメント名。 {@primary The name of the "double" element.} */
    protected static final String ELT_DOUBLE  = "double";

    /** "float" エレメント名。 {@primary The name of the "float" element.} */
    protected static final String ELT_FLOAT   = "float";

    /** "int" エレメント名。 {@primary The name of the "int" element.} */
    protected static final String ELT_INT     = "int";

    /** "long" エレメント名。 {@primary The name of the "long" element.} */
    protected static final String ELT_LONG    = "long";

    /** "short" エレメント名。 {@primary The name of the "short" element.} */
    protected static final String ELT_SHORT   = "short";

    /** "string" エレメント名。 {@primary The name of the "string" element.} */
    protected static final String ELT_STRING  = "string";

    /** "value" 属性名。 {@primary The name of the "value" attribute.} */
    protected static final String ATT_VALUE   = "value";

    class ConstructorDetails {
        private ConstructorDetails _parent = null;
        private Class _type = null;
        private ArrayList _argTypes = null;
        private ArrayList _argValues = null;
        private boolean _isnull = false;

        public ConstructorDetails(String classname, ConstructorDetails parent) throws ClassNotFoundException {
            this(Class.forName(classname),parent,false);
        }

        public ConstructorDetails(String classname, ConstructorDetails parent, boolean isnull) throws ClassNotFoundException {
            this(Class.forName(classname),parent,isnull);
        }

        public ConstructorDetails(Class type, ConstructorDetails parent, boolean isnull) {
            _parent = parent;
            _type = type;
            _argTypes = new ArrayList();
            _argValues = new ArrayList();
            _isnull = isnull;
        }

        public void addArgument(Object value) {
            addArgument(value.getClass(),value);
        }

        public void addArgument(Class type, Object val) {
            if(_isnull) {
                throw new NullPointerException("can't add arguments to null instances");
            }
            _argTypes.add(type);
            _argValues.add(val);
        }

        public Class getType() {
            return _type;
        }

        public ConstructorDetails getParent() {
            return _parent;
        }

        public Object createObject() throws InstantiationException, ClassNotFoundException, IllegalAccessException, InvocationTargetException {
            if(_isnull) {
                return null;
            } else {
                Class k = getType();
                Class[] argtypes = (Class[])_argTypes.toArray(new Class[0]);
                Object[] argvals = _argValues.toArray();
                return ConstructorUtil.invokeConstructor(k,argtypes,argvals);
            }
        }
    }

}

