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.Collection;
035    import java.util.List;
036    import java.util.ArrayList;
037    
038    // to do:
039    //  + add support for strings as CDATA (makes multiline strings easier, for example)
040    //  ? some kind of support for invoking methods?
041    
042    /**
043     * A {@link org.xml.sax.ContentHandler}
044     * for the Java Object Configuration Language.
045     * <p>
046     * JOCL provides an XML syntax for constructing arbitrary Java
047     * {@link java.lang.Object} instances.  It does not define a full
048     * XML document type (there's no root element), but rather an
049     * XML fragment describing the {@link java.lang.Object <tt>Object</tt>s} to be
050     * constructed.
051     * <p>
052     * In a JOCL fragment, one may define a series of objects using
053     * the <tt>object</tt> element.  A trivial example is:
054     * <pre> &lt;object class="java.util.Date"/&gt;</pre>
055     * which constructs an instance of <tt>java.util.Date</tt>
056     * using the no-argument constructor.
057     * <p>
058     * After a "root-level" <tt>&lt;object&gt;</tt> element has been processed
059     * (that is, once {@link #endElement(java.lang.String,java.lang.String,java.lang.String)}
060     * has been invoked by the {@link XMLReader}), it will be appended to a list of <tt>Object</tt>s
061     * maintained by the <tt>JOCLContentHandler</tt>.
062     * <p>
063     * (See {@link #size},
064     * {@link #clear},
065     * {@link #clear(int)},
066     * {@link #getType(int)},
067     * {@link #getValue(int)},
068     * {@link #getTypeArray},
069     * and
070     * {@link #getValueArray}.)
071     * <p>
072     * You can list multiple <tt>object</tt> elements in a fragment.  For example,
073     * after processing the JOCL fragment:
074     * <pre> &lt;object class="java.util.Date"/&gt;
075     * &lt;object class="java.util.Date"/&gt;</pre>
076     * The {@link #getTypeArray} method
077     * will return an array composed
078     * of two instances of <tt>java.util.Date</tt>.  The sequence of
079     * {@link java.lang.Object <tt>Object</tt>s} in the array
080     * will correspond to the sequence of <tt>&lt;object&gt;</tt> elements in the JOCL fragment.
081     * <p>
082     * As we've seen, when used with no child-elements, the <tt>&lt;object&gt;</tt>
083     * tag will cause the no-argument constructor of the specified class to be invoked.
084     * It is also possible to nest <tt>&lt;object&gt;</tt> tags to provide arguments
085     * for the constructor.
086     * For example, the fragment:
087     * <pre> &lt;object class="mypackage.Foo"&gt;
088     *   &lt;object class="mypackage.Bar"/&gt;
089     * &lt;/object&gt;</pre>
090     * will add an instance of <tt>mypackage.Foo</tt> to the object list, constructed via
091     * <tt>new mypackage.Foo(new mypackage.Bar())</tt>.
092     * <p>
093     * There is a special syntax available creating primitive values and arguments,
094     * as well as for constructing {@link java.lang.String <tt>String</tt>}s. Some examples:
095     * <p>
096     * <pre> &lt;byte value="3"/&gt;
097     * &lt;boolean value="false"/&gt;
098     * &lt;char value="c"/&gt;
099     * &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: 883416 $ $Date: 2009-11-23 12:12:14 -0500 (Mon, 23 Nov 2009) $
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    }