View Javadoc

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