001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.commons.jocl;
019:
020: import org.xml.sax.Attributes;
021: import org.xml.sax.InputSource;
022: import org.xml.sax.Locator;
023: import org.xml.sax.SAXException;
024: import org.xml.sax.XMLReader;
025: import org.xml.sax.helpers.DefaultHandler;
026: import org.xml.sax.helpers.XMLReaderFactory;
027: import java.lang.reflect.InvocationTargetException;
028: import java.io.InputStream;
029: import java.io.Reader;
030: import java.io.File;
031: import java.io.FileInputStream;
032: import java.io.FileNotFoundException;
033: import java.io.IOException;
034: import java.util.ArrayList;
035:
036: // to do:
037: // + add support for arrays
038: // + add support for strings as CDATA (makes multiline strings easier, for example)
039: // ? some kind of support for invoking methods?
040:
041: /**
042: * A {@link org.xml.sax.ContentHandler}
043: * for the Java Object Configuration Language.
044: * <p>
045: * JOCL provides an XML syntax for constructing arbitrary Java
046: * {@link java.lang.Object} instances. It does not define a full
047: * XML document type (there's no root element), but rather an
048: * XML fragment describing the {@link java.lang.Object <tt>Object</tt>s} to be
049: * constructed.
050: * <p>
051: * In a JOCL fragment, one may define a series of objects using
052: * the <tt>object</tt> element. A trivial example is:
053: * <pre> <object class="java.util.Date"/></pre>
054: * which constructs an instance of <tt>java.util.Date</tt>
055: * using the no-argument constructor.
056: * <p>
057: * After a "root-level" <tt><object></tt> element has been processed
058: * (that is, once {@link #endElement(java.lang.String,java.lang.String,java.lang.String)}
059: * has been invoked by the {@link XMLReader}), it will be appended to a list of <tt>Object</tt>s
060: * maintained by the <tt>JOCLContentHandler</tt>.
061: * <p>
062: * (See {@link #size},
063: * {@link #clear},
064: * {@link #clear(int)},
065: * {@link #getType(int)},
066: * {@link #getValue(int)},
067: * {@link #getTypeArray},
068: * and
069: * {@link #getValueArray}.)
070: * <p>
071: * You can list multiple <tt>object</tt> elements in a fragment. For example,
072: * after processing the JOCL fragment:
073: * <pre> <object class="java.util.Date"/>
074: * <object class="java.util.Date"/></pre>
075: * The {@link #getTypeArray} method
076: * will return an composed
077: * of two instances of <tt>java.util.Date</tt>. The sequence of
078: * {@link java.lang.Object <tt>Object</tt>s} in the array
079: * will correspond to the sequence of <tt><object></tt> elements in the JOCL fragment.
080: * <p>
081: * As we've seen, when used with no child-elements, the <tt><object></tt>
082: * tag will cause the no-argument constructor of the specified class to be invoked.
083: * It is also possible to nest <tt><object></tt> tags to provide arguments
084: * for the constructor.
085: * For example, the fragment:
086: * <pre> <object class="mypackage.Foo">
087: * <object class="mypackage.Bar"/>
088: * </object></pre>
089: * will add an instance of <tt>mypackage.Foo</tt> to the object list, constructed via
090: * <tt>new mypackage.Foo(new mypackage.Bar())</tt>.
091: * <p>
092: * There is a special syntax available creating primative values and arguments,
093: * as well as for constructing {@link java.lang.String <tt>String</tt>}s. Some examples:
094: * <p>
095: * <pre> <byte value="3"/>
096: * <boolean value="false"/>
097: * <char value="c"/>
098: * <double value="3.14159"/>
099: * <float value="3.14"/>
100: * <int value="17"/>
101: * <long value="1700000"/>
102: * <short value="1"/>
103: * <string value="The quick brown fox..."/></pre>
104: * <p>
105: * When invoked at the "root" level (that is, with no <tt><object></tt> parent),
106: * this will cause the corresponding "object wrapper" to be added to the list of
107: * {@link java.lang.Object <tt>Object</tt>}s. The {@link #getType type} for these
108: * objects will reflect the proper primative type, however. When invoked with an
109: * <tt><object></tt> parent, these will be treated as primitive arguments to the
110: * specified {@link java.lang.Object <tt>Object</tt>}'s constructor. For example, while:
111: * <p>
112: * <pre> <int value="5"/>
113: * <int value="26"/>
114: * <int value="100"/></pre>
115: * <p>
116: * results in three {@link java.lang.Integer} instances being added to the
117: * list of values, with types corresponding to {@link java.lang.Integer}, the fragment:
118: * <p>
119: * <pre> <int value="5"/>
120: * <int value="26"/>
121: * <int value="100"/></pre>
122: * <p>
123: * results in three {@link java.lang.Integer} instances being added to the
124: * list of values, with types corresponding to {@link java.lang.Integer#TYPE}.
125: * <p>
126: * Hence if you want to invoke the <tt>mypackage.Foo(java.lang.Integer,java.lang.Integer,java.lang.Integer)</tt>
127: * constructor, use:
128: * <pre> <object class="mypackage.Foo"/>
129: * <object class="java.lang.Integer"><int value="5"/></object>
130: * <object class="java.lang.Integer"><int value="26"/></object>
131: * <object class="java.lang.Integer"><int value="100"/></object>
132: * </object></pre>
133: * <p>
134: * If you want to invoke the <tt>mypackage.Foo(int,int,int)</tt>
135: * constructor, use:
136: * <pre> <object class="mypackage.Foo"/>
137: * <int value="5"/>
138: * <int value="26"/>
139: * <int value="100"/>
140: * </object></pre>
141: * <p>
142: * If you'd like to creat a <tt>null</tt> object, use:
143: * <pre> <object class="mypackage.Bar" null="true"/></pre>
144: * <p>
145: * Here's a simple but complete example:
146: * <pre> <?xml version="1.0"?>
147: * <arbitrary-root xmlns="http://apache.org/xml/xmlns/jakarta/commons/jocl">
148: * <string value="Hello World!"/>
149: * <string/>
150: * <boolean/>
151: * <boolean value="true"/>
152: * <byte value="1"/>
153: * <short value="1"/>
154: * <int value="1"/>
155: * <long value="1"/>
156: * <float value="1.0"/>
157: * <double value="1.0"/>
158: * <object class="java.util.Date"/>
159: * <object class="java.util.Date">
160: * <int value="1"/>
161: * <int value="1"/>
162: * <int value="1"/>
163: * </object>
164: * </arbitrary-root></pre>
165: * <p>
166: * Formally, a DTD for the JOCL grammar is as follows:
167: * <p>
168: * <pre>
169: * <!ELEMENT object (object|byte|boolean|char|double|float|int|long|short|string)*>
170: * <!ATTLIST object
171: * class CDATA #REQUIRED
172: * null (true|false) "false">
173: *
174: * <!ELEMENT byte EMPTY>
175: * <!ATTLIST byte value CDATA #REQUIRED>
176: *
177: * <!ELEMENT boolean EMPTY>
178: * <!ATTLIST boolean value (true|false) #REQUIRED>
179: *
180: * <!ELEMENT char EMPTY>
181: * <!ATTLIST char value CDATA #REQUIRED>
182: *
183: * <!ELEMENT double EMPTY>
184: * <!ATTLIST double value CDATA #REQUIRED>
185: *
186: * <!ELEMENT float EMPTY>
187: * <!ATTLIST float value CDATA #REQUIRED>
188: *
189: * <!ELEMENT int EMPTY>
190: * <!ATTLIST int value CDATA #REQUIRED>
191: *
192: * <!ELEMENT long EMPTY>
193: * <!ATTLIST long value CDATA #REQUIRED>
194: *
195: * <!ELEMENT short EMPTY>
196: * <!ATTLIST short value CDATA #REQUIRED>
197: *
198: * <!ELEMENT string EMPTY>
199: * <!ATTLIST string value CDATA #REQUIRED>
200: * </pre>
201: * <p>
202: * This class can also be used as a base class for {@link org.xml.sax.ContentHandler}s
203: * that include JOCL as part of their grammar. Simply extend this class, and override the
204: * {@link #startElement},
205: * {@link #characters},
206: * and {@link #endElement} methods to handle
207: * your tags, and invoke the method of the parent class (i.e., <tt>super.<i>XXX</i></tt> for
208: * elements and data that you don't handle.
209: * <p>
210: * A number of static methods are available for simply reading a list of objects from
211: * a {@link InputStream}, {@link Reader} or {@link InputSource}.
212: * <p>
213: * <b>Note that this class is not synchronized.</b>
214: * <p>
215: * @author Rodney Waldhoff
216: * @version $Revision: 491655 $ $Date: 2007-01-01 15:05:30 -0700 (Mon, 01 Jan 2007) $
217: */
218: public class JOCLContentHandler extends DefaultHandler {
219:
220: //--- Static Methods ---------------------------------------------
221: /**
222: * A simple tester method. Reads a JOCL document from standard in
223: * and prints a list of the objects created to standard out.
224: * (Use the <tt>org.xml.sax.driver</tt> system property to specify
225: * an {@link XMLReader}.
226: */
227: public static void main(String[] args) throws Exception {
228: JOCLContentHandler jocl = JOCLContentHandler.parse(System.in,
229: null);
230: for (int i = 0; i < jocl.size(); i++) {
231: System.out.println("<" + jocl.getType(i) + ">\t"
232: + jocl.getValue(i));
233: }
234: }
235:
236: /**
237: * Parses a JOCL document from the specified file, using the
238: * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
239: * property.
240: * The returned {@link JOCLContentHandler} will contain the
241: * list of objects described by the file.
242: * @param f a {@link File} containing the JOCL document
243: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
244: */
245: public static JOCLContentHandler parse(File f) throws SAXException,
246: FileNotFoundException, IOException {
247: return JOCLContentHandler.parse(new FileInputStream(f), null);
248: }
249:
250: /**
251: * Parses a JOCL document from the specified {@link Reader}, using the
252: * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
253: * property.
254: * The returned {@link JOCLContentHandler} will contain the
255: * list of objects described by the file.
256: * @param in a {@link Reader} containing the JOCL document
257: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
258: */
259: public static JOCLContentHandler parse(Reader in)
260: throws SAXException, IOException {
261: return JOCLContentHandler.parse(new InputSource(in), null);
262: }
263:
264: /**
265: * Parses a JOCL document from the specified {@link InputStream}, using the
266: * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
267: * property.
268: * The returned {@link JOCLContentHandler} will contain the
269: * list of objects described by the file.
270: * @param in a {@link InputStream} containing the JOCL document
271: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
272: */
273: public static JOCLContentHandler parse(InputStream in)
274: throws SAXException, IOException {
275: return JOCLContentHandler.parse(new InputSource(in), null);
276: }
277:
278: /**
279: * Parses a JOCL document from the specified {@link InputSource}, using thethe
280: * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
281: * property.
282: * The returned {@link JOCLContentHandler} will contain the
283: * list of objects described by the file.
284: * @param in a {@link InputSource} containing the JOCL document
285: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
286: */
287: public static JOCLContentHandler parse(InputSource in)
288: throws SAXException, IOException {
289: return JOCLContentHandler.parse(in, null);
290: }
291:
292: /**
293: * Parses a JOCL document from the specified file, using the
294: * {@link XMLReader} specified by the <tt>org.xml.sax.driver</tt>
295: * property.
296: * The returned {@link JOCLContentHandler} will contain the
297: * list of objects described by the file.
298: * @param f a {@link File} containing the JOCL document
299: * @param reader the {@link XMLReader} to use to parse the file
300: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
301: */
302: public static JOCLContentHandler parse(File f, XMLReader reader)
303: throws SAXException, FileNotFoundException, IOException {
304: return JOCLContentHandler.parse(new FileInputStream(f), reader);
305: }
306:
307: /**
308: * Parses a JOCL document from the specified {@link Reader}, using the specified
309: * {@link XMLReader}.
310: * The returned {@link JOCLContentHandler} will contain the
311: * list of objects described by the file.
312: * @param in a {@link Reader} containing the JOCL document
313: * @param reader the {@link XMLReader} to use to parse the document
314: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
315: */
316: public static JOCLContentHandler parse(Reader in, XMLReader reader)
317: throws SAXException, IOException {
318: return JOCLContentHandler.parse(new InputSource(in), reader);
319: }
320:
321: /**
322: * Parses a JOCL document from the specified {@link InputStream}, using the specified
323: * {@link XMLReader}.
324: * The returned {@link JOCLContentHandler} will contain the
325: * list of objects described by the file.
326: * @param in a {@link InputStream} containing the JOCL document
327: * @param reader the {@link XMLReader} to use to parse the document
328: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
329: */
330: public static JOCLContentHandler parse(InputStream in,
331: XMLReader reader) throws SAXException, IOException {
332: return JOCLContentHandler.parse(new InputSource(in), reader);
333: }
334:
335: /**
336: * Parses a JOCL document from the specified {@link InputSource}, using the
337: * specified {@link XMLReader}.
338: * The returned {@link JOCLContentHandler} will contain the
339: * list of objects described by the file.
340: * @param in a {@link InputSource} containing the JOCL document
341: * @param reader the {@link XMLReader} to use to parse the document
342: * @return a {@link JOCLContentHandler} containing the list of objects described by the JOCL document
343: */
344: public static JOCLContentHandler parse(InputSource in,
345: XMLReader reader) throws SAXException, IOException {
346: JOCLContentHandler jocl = new JOCLContentHandler();
347: if (null == reader) {
348: reader = XMLReaderFactory.createXMLReader();
349: }
350: reader.setContentHandler(jocl);
351: reader.parse(in);
352: return jocl;
353: }
354:
355: //--- Construtors ------------------------------------------------
356:
357: /**
358: * Equivalent to {@link #JOCLContentHandler(boolean,boolean,boolean,boolean) JOCLContentHandler(true,true,true,true)}.
359: */
360: public JOCLContentHandler() {
361: this (true, true, true, true);
362: }
363:
364: /**
365: * Construct a JOCLContentHandler.
366: * @param emptyEltNS when <tt>true</tt> I should assume any element with an empty namespace is within the JOCL namespace
367: * @param joclEltPrefix 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
368: * @param emptyAttrNS when <tt>true</tt> I should assume any attribute with an empty namespace is within the JOCL namespace
369: * @param joclAttrPrefix 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
370: */
371: public JOCLContentHandler(boolean emptyEltNS,
372: boolean joclEltPrefix, boolean emptyAttrNS,
373: boolean joclAttrPrefix) {
374: _acceptEmptyNamespaceForElements = emptyEltNS;
375: _acceptJoclPrefixForElements = joclEltPrefix;
376: _acceptEmptyNamespaceForAttributes = emptyAttrNS;
377: _acceptJoclPrefixForAttributes = joclAttrPrefix;
378: }
379:
380: //--- Public Methods - Accessing Objects -------------------------
381:
382: /**
383: * Returns the number of values and types in my list.
384: * @return the number of values and types in my list.
385: */
386: public int size() {
387: return _typeList.size();
388: }
389:
390: /**
391: * Clears all the values and types in my list.
392: */
393: public void clear() {
394: _typeList = new ArrayList();
395: _valueList = new ArrayList();
396: }
397:
398: /**
399: * Removes the value/type pair at the specified index.
400: */
401: public void clear(int i) {
402: _typeList.remove(i);
403: _valueList.remove(i);
404: }
405:
406: /**
407: * Returns the type of the object at the specified index.
408: */
409: public Class getType(int i) {
410: return (Class) (_typeList.get(i));
411: }
412:
413: /**
414: * Returns the value of the object at the specified index.
415: */
416: public Object getValue(int i) {
417: return _valueList.get(i);
418: }
419:
420: /**
421: * Returns a shallow copy of my list of values.
422: */
423: public Object[] getValueArray() {
424: return _valueList.toArray();
425: }
426:
427: /**
428: * Returns a shallow copy of my list of types.
429: */
430: public Object[] getTypeArray() {
431: return _typeList.toArray();
432: }
433:
434: //--- Public Methods - DocumentHandler ---------------------------
435:
436: public void startElement(String uri, String localName,
437: String qname, Attributes attr) throws SAXException {
438: try {
439: if (isJoclNamespace(uri, localName, qname)) {
440: if (ELT_OBJECT.equals(localName)) {
441: String cname = getAttributeValue(ATT_CLASS, attr);
442: String isnullstr = getAttributeValue(ATT_ISNULL,
443: attr, "false");
444: boolean isnull = ("true"
445: .equalsIgnoreCase(isnullstr) || "yes"
446: .equalsIgnoreCase(isnullstr));
447: _cur = new ConstructorDetails(cname, _cur, isnull);
448: } else if (ELT_BOOLEAN.equals(localName)) {
449: String valstr = getAttributeValue(ATT_VALUE, attr,
450: "false");
451: boolean val = ("true".equalsIgnoreCase(valstr) || "yes"
452: .equalsIgnoreCase(valstr));
453: addObject(Boolean.TYPE, new Boolean(val));
454: } else if (ELT_BYTE.equals(localName)) {
455: byte val = Byte.parseByte(getAttributeValue(
456: ATT_VALUE, attr, "0"));
457: addObject(Byte.TYPE, new Byte(val));
458: } else if (ELT_CHAR.equals(localName)) {
459: char val = '\u0000';
460: String valstr = getAttributeValue(ATT_VALUE, attr);
461: if (null == valstr) {
462: val = '\u0000';
463: } else if (valstr.length() > 1) {
464: throw new SAXException(
465: "if present, char value must be exactly one character long");
466: } else if (valstr.length() == 1) {
467: val = valstr.charAt(0);
468: } else if (valstr.length() == 0) {
469: throw new SAXException(
470: "if present, char value must be exactly one character long");
471: }
472: addObject(Character.TYPE, new Character(val));
473: } else if (ELT_DOUBLE.equals(localName)) {
474: double val = Double.parseDouble(getAttributeValue(
475: ATT_VALUE, attr, "0"));
476: addObject(Double.TYPE, new Double(val));
477: } else if (ELT_FLOAT.equals(localName)) {
478: float val = Float.parseFloat(getAttributeValue(
479: ATT_VALUE, attr, "0"));
480: addObject(Float.TYPE, new Float(val));
481: } else if (ELT_INT.equals(localName)) {
482: int val = Integer.parseInt(getAttributeValue(
483: ATT_VALUE, attr, "0"));
484: addObject(Integer.TYPE, new Integer(val));
485: } else if (ELT_LONG.equals(localName)) {
486: long val = Long.parseLong(getAttributeValue(
487: ATT_VALUE, attr, "0"));
488: addObject(Long.TYPE, new Long(val));
489: } else if (ELT_SHORT.equals(localName)) {
490: short val = Short.parseShort(getAttributeValue(
491: ATT_VALUE, attr, "0"));
492: addObject(Short.TYPE, new Short(val));
493: } else if (ELT_STRING.equals(localName)) {
494: String val = getAttributeValue(ATT_VALUE, attr);
495: addObject("".getClass(), val);
496: } else {
497: // unrecognized JOCL element warning?
498: }
499: }
500: } catch (Exception e) {
501: throw new SAXException(e);
502: }
503: }
504:
505: public void endElement(String uri, String localName, String qname)
506: throws SAXException {
507: try {
508: if (isJoclNamespace(uri, localName, qname)) {
509: if (ELT_OBJECT.equals(localName)) {
510: ConstructorDetails temp = _cur;
511: _cur = _cur.getParent();
512: if (null == _cur) {
513: _typeList.add(temp.getType());
514: _valueList.add(temp.createObject());
515: } else {
516: _cur.addArgument(temp.getType(), temp
517: .createObject());
518: }
519: }
520: /*
521: else if(ELT_BOOLEAN.equals(localName)) {
522: // nothing to do here
523: } else if(ELT_BYTE.equals(localName)) {
524: // nothing to do here
525: } else if(ELT_CHAR.equals(localName)) {
526: // nothing to do here
527: } else if(ELT_DOUBLE.equals(localName)) {
528: // nothing to do here
529: } else if(ELT_FLOAT.equals(localName)) {
530: // nothing to do here
531: } else if(ELT_INT.equals(localName)) {
532: // nothing to do here
533: } else if(ELT_LONG.equals(localName)) {
534: // nothing to do here
535: } else if(ELT_SHORT.equals(localName)) {
536: // nothing to do here
537: } else if(ELT_STRING.equals(localName)) {
538: // nothing to do here
539: } else {
540: // unrecognized JOCL element warning?
541: }
542: */
543: }
544: } catch (Exception e) {
545: throw new SAXException(e);
546: }
547: }
548:
549: public void setDocumentLocator(Locator locator) {
550: _locator = locator;
551: }
552:
553: //--- Protected Methods ------------------------------------------
554:
555: /**
556: * Returns <tt>true</tt> if the given attributes define an
557: * element within the JOCL namespace (according to my current
558: * configuration.)
559: *
560: * @see #_acceptEmptyNamespaceForElements
561: * @see #_acceptJoclPrefixForElements
562: */
563: protected boolean isJoclNamespace(String uri, String localname,
564: String qname) {
565: if (JOCL_NAMESPACE_URI.equals(uri)) {
566: return true;
567: } else if (_acceptEmptyNamespaceForElements
568: && (null == uri || "".equals(uri))) {
569: return true;
570: } else if (_acceptJoclPrefixForElements
571: && (null == uri || "".equals(uri))
572: && qname.startsWith(JOCL_PREFIX)) {
573: return true;
574: } else {
575: return false;
576: }
577: }
578:
579: /**
580: * Equivalent to {@link #getAttributeValue(java.lang.String,org.xml.sax.Attributes,java.lang.String) <tt>getAttributeValue(localname,attr,null)</tt>}.
581: */
582: protected String getAttributeValue(String localname, Attributes attr) {
583: return getAttributeValue(localname, attr, null);
584: }
585:
586: /**
587: * Returns the value of attribute with the given
588: * <tt><i>localname</i></tt> within the JOCL
589: * namespace from the given set of {@link Attributes}.
590: * If no such attribute can be found, returns
591: * <tt><i>implied</i></tt>.
592: *
593: * @param localname the unqualified name of the attribute to look for
594: * @param attr the Attributes in which to find the value
595: * @param implied the default value for the attribute
596: * @return the value of attribute with the given
597: * <tt><i>localname</i></tt> within the JOCL
598: * namespace from the given set of {@link Attributes}.
599: * If no such attribute can be found, returns
600: * <tt><i>implied</i></tt>.
601: */
602: protected String getAttributeValue(String localname,
603: Attributes attr, String implied) {
604: String val = attr.getValue(JOCL_NAMESPACE_URI, localname);
605: if (null == val && _acceptEmptyNamespaceForAttributes) {
606: val = attr.getValue("", localname);
607: }
608: if (null == val && _acceptJoclPrefixForAttributes) {
609: val = attr.getValue("", JOCL_PREFIX + localname);
610: }
611: return (null == val ? implied : val);
612: }
613:
614: /**
615: * Add the specified object either to my type/value list, or
616: * as an argument to the object I'm currently constructing.
617: */
618: protected void addObject(Class type, Object val) {
619: if (null == _cur) {
620: _typeList.add(type);
621: _valueList.add(val);
622: } else {
623: _cur.addArgument(type, val);
624: }
625: }
626:
627: //--- Protected Attributes ---------------------------------------
628:
629: /**
630: * The JOCL namespace URI, <tt>http://apache.org/xml/xmlns/jakarta/commons/jocl</tt>.
631: */
632: public static final String JOCL_NAMESPACE_URI = "http://apache.org/xml/xmlns/jakarta/commons/jocl";
633:
634: /**
635: * The default JOCL prefix, <tt>jocl:</tt>.
636: */
637: public static final String JOCL_PREFIX = "jocl:";
638:
639: /**
640: * A list of the types ({@link Class}es) already created via the parse.
641: */
642: protected ArrayList _typeList = new ArrayList();
643:
644: /**
645: * A list of the values ({@link Object}s) already created via the parse.
646: */
647: protected ArrayList _valueList = new ArrayList();
648:
649: /**
650: * The object I'm currently working on.
651: */
652: protected ConstructorDetails _cur = null;
653:
654: /**
655: * When <tt>true</tt>, I will treat elements with an
656: * empty namespace URI as part of the JOCL namespace.
657: *
658: * @see #JOCL_NAMESPACE_URI
659: */
660: protected boolean _acceptEmptyNamespaceForElements = true;
661:
662: /**
663: * When <tt>true</tt>, I will treat elements with the
664: * {@link #JOCL_PREFIX} but no namespace URI as being
665: * mapped to the jocl namespace.
666: *
667: * @see #JOCL_PREFIX
668: * @see #JOCL_NAMESPACE_URI
669: */
670: protected boolean _acceptJoclPrefixForElements = true;
671:
672: /**
673: * When <tt>true</tt>, I will treat attributes with an
674: * empty namespace URI as part of the JOCL namespace.
675: *
676: * @see #JOCL_NAMESPACE_URI
677: */
678: protected boolean _acceptEmptyNamespaceForAttributes = true;
679:
680: /**
681: * When <tt>true</tt>, I will treat attributes with the
682: * {@link #JOCL_PREFIX} but no namespace URI as being
683: * mapped to the jocl namespace.
684: *
685: * @see #JOCL_PREFIX
686: * @see #JOCL_NAMESPACE_URI
687: */
688: protected boolean _acceptJoclPrefixForAttributes = true;
689:
690: /** My {@link Locator}. */
691: protected Locator _locator = null;
692:
693: /** The name of the "object" element. */
694: protected static final String ELT_OBJECT = "object";
695:
696: /** The name of the "object" element's "class" attribute. */
697: protected static final String ATT_CLASS = "class";
698:
699: /** The name of the "object" element's "isnull" attribute. */
700: protected static final String ATT_ISNULL = "null";
701:
702: /** The name of the "boolean" element. */
703: protected static final String ELT_BOOLEAN = "boolean";
704:
705: /** The name of the "byte" element. */
706: protected static final String ELT_BYTE = "byte";
707:
708: /** The name of the "char" element. */
709: protected static final String ELT_CHAR = "char";
710:
711: /** The name of the "double" element. */
712: protected static final String ELT_DOUBLE = "double";
713:
714: /** The name of the "float" element. */
715: protected static final String ELT_FLOAT = "float";
716:
717: /** The name of the "int" element. */
718: protected static final String ELT_INT = "int";
719:
720: /** The name of the "long" element. */
721: protected static final String ELT_LONG = "long";
722:
723: /** The name of the "short" element. */
724: protected static final String ELT_SHORT = "short";
725:
726: /** The name of the "string" element. */
727: protected static final String ELT_STRING = "string";
728:
729: /** The name of the "value" attribute. */
730: protected static final String ATT_VALUE = "value";
731:
732: class ConstructorDetails {
733: private ConstructorDetails _parent = null;
734: private Class _type = null;
735: private ArrayList _argTypes = null;
736: private ArrayList _argValues = null;
737: private boolean _isnull = false;
738:
739: public ConstructorDetails(String classname,
740: ConstructorDetails parent)
741: throws ClassNotFoundException {
742: this (Class.forName(classname), parent, false);
743: }
744:
745: public ConstructorDetails(String classname,
746: ConstructorDetails parent, boolean isnull)
747: throws ClassNotFoundException {
748: this (Class.forName(classname), parent, isnull);
749: }
750:
751: public ConstructorDetails(Class type,
752: ConstructorDetails parent, boolean isnull) {
753: _parent = parent;
754: _type = type;
755: _argTypes = new ArrayList();
756: _argValues = new ArrayList();
757: _isnull = isnull;
758: }
759:
760: public void addArgument(Object value) {
761: addArgument(value.getClass(), value);
762: }
763:
764: public void addArgument(Class type, Object val) {
765: if (_isnull) {
766: throw new NullPointerException(
767: "can't add arguments to null instances");
768: }
769: _argTypes.add(type);
770: _argValues.add(val);
771: }
772:
773: public Class getType() {
774: return _type;
775: }
776:
777: public ConstructorDetails getParent() {
778: return _parent;
779: }
780:
781: public Object createObject() throws InstantiationException,
782: ClassNotFoundException, IllegalAccessException,
783: InvocationTargetException {
784: if (_isnull) {
785: return null;
786: } else {
787: Class k = getType();
788: Class[] argtypes = (Class[]) _argTypes
789: .toArray(new Class[0]);
790: Object[] argvals = _argValues.toArray();
791: return ConstructorUtil.invokeConstructor(k, argtypes,
792: argvals);
793: }
794: }
795: }
796:
797: }
|