org.apache.tomcat.dbcp.jocl.JOCLContentHandler Maven / Gradle / Ivy
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.tomcat.dbcp.jocl;
import org.xml.sax.Attributes;
import org.xml.sax.InputSource;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;
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;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
// to do:
// + add support for strings as CDATA (makes multiline strings easier, for example)
// ? some kind of support for invoking methods?
/**
* A {@link org.xml.sax.ContentHandler}
* for the Java Object Configuration Language.
*
* 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 Objects} to be
* constructed.
*
* In a JOCL fragment, one may define a series of objects using
* the object element. A trivial example is:
*
<object class="java.util.Date"/>
* which constructs an instance of java.util.Date
* using the no-argument constructor.
*
* After a "root-level" <object> 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 Objects
* maintained by the JOCLContentHandler.
*
* (See {@link #size},
* {@link #clear},
* {@link #clear(int)},
* {@link #getType(int)},
* {@link #getValue(int)},
* {@link #getTypeArray},
* and
* {@link #getValueArray}.)
*
* You can list multiple object elements in a fragment. For example,
* after processing the JOCL fragment:
*
<object class="java.util.Date"/>
* <object class="java.util.Date"/>
* The {@link #getTypeArray} method
* will return an array composed
* of two instances of java.util.Date. The sequence of
* {@link java.lang.Object Objects} in the array
* will correspond to the sequence of <object> elements in the JOCL fragment.
*
* As we've seen, when used with no child-elements, the <object>
* tag will cause the no-argument constructor of the specified class to be invoked.
* It is also possible to nest <object> tags to provide arguments
* for the constructor.
* For example, the fragment:
*
<object class="mypackage.Foo">
* <object class="mypackage.Bar"/>
* </object>
* will add an instance of mypackage.Foo to the object list, constructed via
* new mypackage.Foo(new mypackage.Bar()).
*
* There is a special syntax available creating primitive values and arguments,
* as well as for constructing {@link java.lang.String String}s. Some examples:
*
*
<byte value="3"/>
* <boolean value="false"/>
* <char value="c"/>
* <double value="3.14159"/>
* <float value="3.14"/>
* <int value="17"/>
* <long value="1700000"/>
* <short value="1"/>
* <string value="The quick brown fox..."/>
*
* When invoked at the "root" level (that is, with no <object> parent),
* this will cause the corresponding "object wrapper" to be added to the list of
* {@link java.lang.Object Object}s. The {@link #getType type} for these
* objects will reflect the proper primitive type, however. When invoked with an
* <object> parent, these will be treated as primitive arguments to the
* specified {@link java.lang.Object Object}'s constructor. For example, while:
*
*
<int value="5"/>
* <int value="26"/>
* <int value="100"/>
*
* 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:
*
*
<int value="5"/>
* <int value="26"/>
* <int value="100"/>
*
* results in three {@link java.lang.Integer} instances being added to the
* list of values, with types corresponding to {@link java.lang.Integer#TYPE}.
*
* Hence if you want to invoke the mypackage.Foo(java.lang.Integer,java.lang.Integer,java.lang.Integer)
* constructor, use:
*
<object class="mypackage.Foo"/>
* <object class="java.lang.Integer"><int value="5"/></object>
* <object class="java.lang.Integer"><int value="26"/></object>
* <object class="java.lang.Integer"><int value="100"/></object>
* </object>
*
* If you want to invoke the mypackage.Foo(int,int,int)
* constructor, use:
*
<object class="mypackage.Foo"/>
* <int value="5"/>
* <int value="26"/>
* <int value="100"/>
* </object>
*
* If you'd like to creat a null object, use:
*
<object class="mypackage.Bar" null="true"/>
*
* Here's a simple but complete example:
*
<?xml version="1.0"?>
* <arbitrary-root xmlns="http://apache.org/xml/xmlns/jakarta/commons/jocl">
* <string value="Hello World!"/>
* <string/>
* <boolean/>
* <boolean value="true"/>
* <byte value="1"/>
* <short value="1"/>
* <int value="1"/>
* <long value="1"/>
* <float value="1.0"/>
* <double value="1.0"/>
* <object class="java.util.Date"/>
* <object class="java.util.Date">
* <int value="1"/>
* <int value="1"/>
* <int value="1"/>
* </object>
* </arbitrary-root>
*
* Formally, a DTD for the JOCL grammar is as follows:
*
*
* <!ELEMENT object (object|array|collection|list|byte|boolean|char|double|float|int|long|short|string)*>
* <!ATTLIST object
* class CDATA #REQUIRED
* null (true|false) "false">
*
* <!ELEMENT byte EMPTY>
* <!ATTLIST byte value CDATA #REQUIRED>
*
* <!ELEMENT boolean EMPTY>
* <!ATTLIST boolean value (true|false) #REQUIRED>
*
* <!ELEMENT char EMPTY>
* <!ATTLIST char value CDATA #REQUIRED>
*
* <!ELEMENT double EMPTY>
* <!ATTLIST double value CDATA #REQUIRED>
*
* <!ELEMENT float EMPTY>
* <!ATTLIST float value CDATA #REQUIRED>
*
* <!ELEMENT int EMPTY>
* <!ATTLIST int value CDATA #REQUIRED>
*
* <!ELEMENT long EMPTY>
* <!ATTLIST long value CDATA #REQUIRED>
*
* <!ELEMENT short EMPTY>
* <!ATTLIST short value CDATA #REQUIRED>
*
* <!ELEMENT string EMPTY>
* <!ATTLIST string value CDATA #REQUIRED>
*
*
* 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., super.XXX for
* elements and data that you don't handle.
*
* A number of static methods are available for simply reading a list of objects from
* a {@link InputStream}, {@link Reader} or {@link InputSource}.
*
* Note that this class is not synchronized.
*
* @author Rodney Waldhoff
* @version $Revision: 883416 $ $Date: 2009-11-23 12:12:14 -0500 (Mon, 23 Nov 2009) $
*/
public class JOCLContentHandler extends DefaultHandler {
//--- Static Methods ---------------------------------------------
/**
* A simple tester method. Reads a JOCL document from standard in
* and prints a list of the objects created to standard out.
* (Use the org.xml.sax.driver 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\t" + jocl.getValue(i));
}
}
/**
* Parses a JOCL document from the specified file, using the
* {@link XMLReader} specified by the org.xml.sax.driver
* property.
* The returned {@link JOCLContentHandler} will contain the
* list of objects described by the file.
* @param f a {@link File} containing the JOCL document
* @return 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);
}
/**
* Parses a JOCL document from the specified {@link Reader}, using the
* {@link XMLReader} specified by the org.xml.sax.driver
* property.
* The returned {@link JOCLContentHandler} will contain the
* list of objects described by the file.
* @param in a {@link Reader} containing the JOCL document
* @return 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);
}
/**
* Parses a JOCL document from the specified {@link InputStream}, using the
* {@link XMLReader} specified by the org.xml.sax.driver
* property.
* The returned {@link JOCLContentHandler} will contain the
* list of objects described by the file.
* @param in a {@link InputStream} containing the JOCL document
* @return 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);
}
/**
* Parses a JOCL document from the specified {@link InputSource}, using thethe
* {@link XMLReader} specified by the org.xml.sax.driver
* property.
* The returned {@link JOCLContentHandler} will contain the
* list of objects described by the file.
* @param in a {@link InputSource} containing the JOCL document
* @return 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);
}
/**
* Parses a JOCL document from the specified file, using the
* {@link XMLReader} specified by the org.xml.sax.driver
* property.
* The returned {@link JOCLContentHandler} will contain the
* list of objects described by the file.
* @param f a {@link File} containing the JOCL document
* @param reader the {@link XMLReader} to use to parse the file
* @return 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);
}
/**
* 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 a {@link Reader} containing the JOCL document
* @param reader the {@link XMLReader} to use to parse the document
* @return 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);
}
/**
* 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 a {@link InputStream} containing the JOCL document
* @param reader the {@link XMLReader} to use to parse the document
* @return 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);
}
/**
* 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 a {@link InputSource} containing the JOCL document
* @param reader the {@link XMLReader} to use to parse the document
* @return 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 ------------------------------------------------
/**
* Equivalent to {@link #JOCLContentHandler(boolean,boolean,boolean,boolean) JOCLContentHandler(true,true,true,true)}.
*/
public JOCLContentHandler() {
this(true,true,true,true);
}
/**
* Construct a JOCLContentHandler.
* @param emptyEltNS when true I should assume any element with an empty namespace is within the JOCL namespace
* @param joclEltPrefix when true I should assume any element who's prefix is jocl: and who's namespace is empty is within the JOCL namespace
* @param emptyAttrNS when true I should assume any attribute with an empty namespace is within the JOCL namespace
* @param joclAttrPrefix when true I should assume any attribute who's prefix is jocl: 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 -------------------------
/**
* Returns the number of values and types in my list.
* @return the number of values and types in my list.
*/
public int size() {
return _typeList.size();
}
/**
* Clears all the values and types in my list.
*/
public void clear() {
_typeList = new ArrayList();
_valueList = new ArrayList();
}
/**
* Removes the value/type pair at the specified index.
*/
public void clear(int i) {
_typeList.remove(i);
_valueList.remove(i);
}
/**
* Returns the type of the object at the specified index.
*/
public Class getType(int i) {
return(Class)(_typeList.get(i));
}
/**
* Returns the value of the object at the specified index.
*/
public Object getValue(int i) {
return _valueList.get(i);
}
/**
* Returns a shallow copy of my list of values.
*/
public Object[] getValueArray() {
return _valueList.toArray();
}
/**
* 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_ARRAY.equals(localName)) {
_cur = new ConstructorDetails(Object[].class,_cur,false,true);
} else if(ELT_COLLECTION.equals(localName)) {
_cur = new ConstructorDetails(Collection.class,_cur,false,true);
} else if(ELT_LIST.equals(localName)) {
_cur = new ConstructorDetails(List.class,_cur,false,true);
} else if(ELT_BOOLEAN.equals(localName)) {
String valstr = getAttributeValue(ATT_VALUE,attr,"false");
boolean val = ("true".equalsIgnoreCase(valstr) || "yes".equalsIgnoreCase(valstr));
addObject(Boolean.TYPE,Boolean.valueOf(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(NumberFormatException e) {
throw new SAXException(e);
} catch(ClassNotFoundException 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) || ELT_ARRAY.equals(localName)
|| ELT_COLLECTION.equals(localName) || ELT_LIST.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 ------------------------------------------
/**
* Returns true 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;
}
}
/**
* Equivalent to {@link #getAttributeValue(java.lang.String,org.xml.sax.Attributes,java.lang.String) getAttributeValue(localname,attr,null)}.
*/
protected String getAttributeValue(String localname, Attributes attr) {
return getAttributeValue(localname,attr,null);
}
/**
* Returns the value of attribute with the given
* localname within the JOCL
* namespace from the given set of {@link Attributes}.
* If no such attribute can be found, returns
* implied.
*
* @param localname the unqualified name of the attribute to look for
* @param attr the Attributes in which to find the value
* @param implied the default value for the attribute
* @return the value of attribute with the given
* localname within the JOCL
* namespace from the given set of {@link Attributes}.
* If no such attribute can be found, returns
* implied.
*/
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);
}
/**
* 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 ---------------------------------------
/**
* The JOCL namespace URI, http://apache.org/xml/xmlns/jakarta/commons/jocl.
*/
public static final String JOCL_NAMESPACE_URI = "http://apache.org/xml/xmlns/jakarta/commons/jocl";
/**
* The default JOCL prefix, jocl:.
*/
public static final String JOCL_PREFIX = "jocl:";
/**
* A list of the types ({@link Class}es) already created via the parse.
*/
protected ArrayList _typeList = new ArrayList();
/**
* A list of the values ({@link Object}s) already created via the parse.
*/
protected ArrayList _valueList = new ArrayList();
/**
* The object I'm currently working on.
*/
protected ConstructorDetails _cur = null;
/**
* When true, I will treat elements with an
* empty namespace URI as part of the JOCL namespace.
*
* @see #JOCL_NAMESPACE_URI
*/
protected boolean _acceptEmptyNamespaceForElements = true;
/**
* When true, 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;
/**
* When true, I will treat attributes with an
* empty namespace URI as part of the JOCL namespace.
*
* @see #JOCL_NAMESPACE_URI
*/
protected boolean _acceptEmptyNamespaceForAttributes = true;
/**
* When true, 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;
/** My {@link Locator}. */
protected Locator _locator = null;
/** The name of the "object" element. */
protected static final String ELT_OBJECT = "object";
/** The name of the "array" element.
* @since 1.2.2
*/
protected static final String ELT_ARRAY = "array";
/** The name of the "collection" element.
* @since 1.2.2
*/
protected static final String ELT_COLLECTION = "collection";
/** The name of the "list" element.
* @since 1.2.2
*/
protected static final String ELT_LIST = "list";
/** The name of the "object" element's "class" attribute. */
protected static final String ATT_CLASS = "class";
/** The name of the "object" element's "isnull" attribute. */
protected static final String ATT_ISNULL = "null";
/** The name of the "boolean" element. */
protected static final String ELT_BOOLEAN = "boolean";
/** The name of the "byte" element. */
protected static final String ELT_BYTE = "byte";
/** The name of the "char" element. */
protected static final String ELT_CHAR = "char";
/** The name of the "double" element. */
protected static final String ELT_DOUBLE = "double";
/** The name of the "float" element. */
protected static final String ELT_FLOAT = "float";
/** The name of the "int" element. */
protected static final String ELT_INT = "int";
/** The name of the "long" element. */
protected static final String ELT_LONG = "long";
/** The name of the "short" element. */
protected static final String ELT_SHORT = "short";
/** The name of the "string" element. */
protected static final String ELT_STRING = "string";
/** The name of the "value" attribute. */
protected static final String ATT_VALUE = "value";
static class ConstructorDetails {
private ConstructorDetails _parent = null;
private Class _type = null;
private ArrayList _argTypes = null;
private ArrayList _argValues = null;
private boolean _isnull = false;
private boolean _isgroup = false;
public ConstructorDetails(String classname, ConstructorDetails parent) throws ClassNotFoundException {
this(Class.forName(classname),parent,false,false);
}
public ConstructorDetails(String classname, ConstructorDetails parent, boolean isnull) throws ClassNotFoundException {
this(Class.forName(classname),parent,isnull,false);
}
/**
* @since 1.3
*/
public ConstructorDetails(String classname, ConstructorDetails parent, boolean isnull, boolean isgroup) throws ClassNotFoundException {
this(Class.forName(classname),parent,isnull,isgroup);
}
/**
* @since 1.3
*/
public ConstructorDetails(Class type, ConstructorDetails parent, boolean isnull, boolean isgroup) {
_parent = parent;
_type = type;
_argTypes = new ArrayList();
_argValues = new ArrayList();
_isnull = isnull;
_isgroup = isgroup;
}
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, IllegalAccessException, InvocationTargetException {
if(_isnull) {
return null;
} else if( _isgroup ) {
if (_type.equals(Object[].class)) {
return _argValues.toArray();
} else if (_type.equals(Collection.class) || _type.equals(List.class)) {
return _argValues;
} else {
throw new IllegalStateException("implementation error: unhandled _type:" + _type);
}
} else {
Class k = getType();
Class[] argtypes = (Class[])_argTypes.toArray(new Class[0]);
Object[] argvals = _argValues.toArray();
return ConstructorUtil.invokeConstructor(k,argtypes,argvals);
}
}
}
}