Coverage Report - org.apache.commons.jocl.JOCLContentHandler
 
Classes in this File Line Coverage Branch Coverage Complexity
JOCLContentHandler
74%
98/132
64%
57/88
2.882
JOCLContentHandler$ConstructorDetails
80%
33/41
83%
10/12
2.882
 
 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  0
         JOCLContentHandler jocl = JOCLContentHandler.parse(System.in,null);
 230  0
         for(int i=0;i<jocl.size();i++) {
 231  0
             System.out.println("<" + jocl.getType(i) + ">\t" + jocl.getValue(i));
 232  
         }
 233  0
     }
 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  0
         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  0
         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  6
         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  0
         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  0
         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  0
         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  0
         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  6
         JOCLContentHandler jocl = new JOCLContentHandler();
 338  6
         if(null == reader) {
 339  6
             reader = XMLReaderFactory.createXMLReader();
 340  
         }
 341  6
         reader.setContentHandler(jocl);
 342  6
         reader.parse(in);
 343  6
         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  18
         this(true,true,true,true);
 353  18
     }
 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  18
     public JOCLContentHandler(boolean emptyEltNS, boolean joclEltPrefix, boolean emptyAttrNS, boolean joclAttrPrefix) {
 363  18
         _acceptEmptyNamespaceForElements = emptyEltNS;
 364  18
         _acceptJoclPrefixForElements = joclEltPrefix;
 365  18
         _acceptEmptyNamespaceForAttributes = emptyAttrNS;
 366  18
         _acceptJoclPrefixForAttributes = joclAttrPrefix;
 367  18
     }
 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  0
         return _typeList.size();
 377  
     }
 378  
 
 379  
     /**
 380  
      * Clears all the values and types in my list.
 381  
      */
 382  
     public void clear() {
 383  0
         _typeList = new ArrayList();
 384  0
         _valueList = new ArrayList();
 385  0
     }
 386  
 
 387  
     /**
 388  
      * Removes the value/type pair at the specified index.
 389  
      */
 390  
     public void clear(int i) {
 391  0
         _typeList.remove(i);
 392  0
         _valueList.remove(i);
 393  0
     }
 394  
 
 395  
     /**
 396  
      * Returns the type of the object at the specified index.
 397  
      */
 398  
     public Class getType(int i) {
 399  62
         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  62
         return _valueList.get(i);
 407  
     }
 408  
 
 409  
     /**
 410  
      * Returns a shallow copy of my list of values.
 411  
      */
 412  
     public Object[] getValueArray() {
 413  0
         return _valueList.toArray();
 414  
     }
 415  
 
 416  
     /**
 417  
      * Returns a shallow copy of my list of types.
 418  
      */
 419  
     public Object[] getTypeArray() {
 420  0
         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  306
             if(isJoclNamespace(uri,localName,qname)) {
 428  306
                 if(ELT_OBJECT.equals(localName)) {
 429  68
                     String cname = getAttributeValue(ATT_CLASS,attr);
 430  68
                     String isnullstr = getAttributeValue(ATT_ISNULL,attr,"false");
 431  68
                     boolean isnull = ("true".equalsIgnoreCase(isnullstr) || "yes".equalsIgnoreCase(isnullstr));
 432  68
                     _cur = new ConstructorDetails(cname,_cur,isnull);
 433  68
                 } else if(ELT_ARRAY.equals(localName)) {
 434  4
                     _cur = new ConstructorDetails(Object[].class,_cur,false,true);
 435  234
                 } else if(ELT_COLLECTION.equals(localName)) {
 436  10
                     _cur = new ConstructorDetails(Collection.class,_cur,false,true);
 437  224
                 } else if(ELT_LIST.equals(localName)) {
 438  4
                     _cur = new ConstructorDetails(List.class,_cur,false,true);
 439  220
                 } else if(ELT_BOOLEAN.equals(localName)) {
 440  52
                     String valstr = getAttributeValue(ATT_VALUE,attr,"false");
 441  52
                     boolean val = ("true".equalsIgnoreCase(valstr) || "yes".equalsIgnoreCase(valstr));
 442  52
                     addObject(Boolean.TYPE,Boolean.valueOf(val));
 443  52
                 } else if(ELT_BYTE.equals(localName)) {
 444  16
                     byte val = Byte.parseByte(getAttributeValue(ATT_VALUE,attr,"0"));
 445  16
                     addObject(Byte.TYPE,new Byte(val));
 446  16
                 } else if(ELT_CHAR.equals(localName)) {
 447  4
                     char val = '\u0000';
 448  4
                     String valstr = getAttributeValue(ATT_VALUE,attr);
 449  4
                     if(null == valstr) {
 450  0
                         val = '\u0000';
 451  4
                     } else if(valstr.length() > 1) {
 452  0
                         throw new SAXException("if present, char value must be exactly one character long");
 453  4
                     } else if(valstr.length()==1) {
 454  4
                         val = valstr.charAt(0);
 455  0
                     } else if(valstr.length()==0) {
 456  0
                         throw new SAXException("if present, char value must be exactly one character long");
 457  
                     }
 458  4
                     addObject(Character.TYPE,new Character(val));
 459  4
                 } else if(ELT_DOUBLE.equals(localName)) {
 460  4
                     double val = Double.parseDouble(getAttributeValue(ATT_VALUE,attr,"0"));
 461  4
                     addObject(Double.TYPE,new Double(val));
 462  4
                 } else if(ELT_FLOAT.equals(localName)) {
 463  4
                     float val = Float.parseFloat(getAttributeValue(ATT_VALUE,attr,"0"));
 464  4
                     addObject(Float.TYPE,new Float(val));
 465  4
                 } else if(ELT_INT.equals(localName)) {
 466  40
                     int val = Integer.parseInt(getAttributeValue(ATT_VALUE,attr,"0"));
 467  40
                     addObject(Integer.TYPE,new Integer(val));
 468  40
                 } else if(ELT_LONG.equals(localName)) {
 469  56
                     long val = Long.parseLong(getAttributeValue(ATT_VALUE,attr,"0"));
 470  56
                     addObject(Long.TYPE,new Long(val));
 471  56
                 } else if(ELT_SHORT.equals(localName)) {
 472  4
                     short val = Short.parseShort(getAttributeValue(ATT_VALUE,attr,"0"));
 473  4
                     addObject(Short.TYPE,new Short(val));
 474  4
                 } else if(ELT_STRING.equals(localName)) {
 475  32
                     String val = getAttributeValue(ATT_VALUE,attr);
 476  32
                     addObject("".getClass(),val);
 477  
                 } else {
 478  
                     // unrecognized JOCL element warning?
 479  
                 }
 480  
             }
 481  0
         } catch(NumberFormatException e) {
 482  0
             throw new SAXException(e);
 483  0
         } catch(ClassNotFoundException e) {
 484  0
             throw new SAXException(e);
 485  306
         }
 486  306
     }
 487  
 
 488  
     public void endElement(String uri, String localName, String qname) throws SAXException {
 489  
         try {
 490  306
             if(isJoclNamespace(uri,localName,qname)) {
 491  306
                 if(ELT_OBJECT.equals(localName) || ELT_ARRAY.equals(localName)
 492  
                     || ELT_COLLECTION.equals(localName) || ELT_LIST.equals(localName)) {
 493  86
                     ConstructorDetails temp = _cur;
 494  86
                     _cur = _cur.getParent();
 495  86
                     if(null == _cur) {
 496  30
                         _typeList.add(temp.getType());
 497  30
                         _valueList.add(temp.createObject());
 498  
                     } else {
 499  56
                         _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  0
         } catch(Exception e) {
 527  0
             throw new SAXException(e);
 528  306
         }
 529  306
     }
 530  
 
 531  
     public void setDocumentLocator(Locator locator) {
 532  6
         _locator = locator;
 533  6
     }
 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  612
         if(JOCL_NAMESPACE_URI.equals(uri)) {
 547  612
             return true;
 548  0
         } else if(_acceptEmptyNamespaceForElements && (null == uri || "".equals(uri))) {
 549  0
             return true;
 550  0
         } else if(_acceptJoclPrefixForElements && (null == uri || "".equals(uri)) && qname.startsWith(JOCL_PREFIX)) {
 551  0
             return true;
 552  
         } else {
 553  0
             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  104
         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  348
         String val = attr.getValue(JOCL_NAMESPACE_URI,localname);
 582  348
         if(null == val && _acceptEmptyNamespaceForAttributes) {
 583  264
             val = attr.getValue("",localname);
 584  
         }
 585  348
         if(null == val && _acceptJoclPrefixForAttributes) {
 586  42
             val = attr.getValue("",JOCL_PREFIX + localname);
 587  
         }
 588  348
         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  212
         if(null == _cur) {
 597  36
             _typeList.add(type);
 598  36
             _valueList.add(val);
 599  
         } else {
 600  176
             _cur.addArgument(type,val);
 601  
         }
 602  212
     }
 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  18
     protected ArrayList _typeList = new ArrayList();
 620  
 
 621  
     /**
 622  
      * A list of the values ({@link Object}s) already created via the parse.
 623  
      */
 624  18
     protected ArrayList _valueList = new ArrayList();
 625  
 
 626  
     /**
 627  
      * The object I'm currently working on.
 628  
      */
 629  18
     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  18
     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  18
     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  18
     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  18
     protected boolean _acceptJoclPrefixForAttributes = true;
 666  
 
 667  
     /** My {@link Locator}. */
 668  18
     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  86
         private ConstructorDetails _parent = null;
 726  86
         private Class _type = null;
 727  86
         private ArrayList _argTypes = null;
 728  86
         private ArrayList _argValues = null;
 729  86
         private boolean _isnull = false;
 730  86
         private boolean _isgroup = false;
 731  
 
 732  
         public ConstructorDetails(String classname, ConstructorDetails parent) throws ClassNotFoundException {
 733  0
             this(Class.forName(classname),parent,false,false);
 734  0
         }
 735  
 
 736  
         public ConstructorDetails(String classname, ConstructorDetails parent, boolean isnull) throws ClassNotFoundException {
 737  68
             this(Class.forName(classname),parent,isnull,false);
 738  68
         }
 739  
 
 740  
         /**
 741  
          * @since 1.3
 742  
          */
 743  
         public ConstructorDetails(String classname, ConstructorDetails parent, boolean isnull, boolean isgroup) throws ClassNotFoundException {
 744  0
             this(Class.forName(classname),parent,isnull,isgroup);
 745  0
         }
 746  
 
 747  
         /**
 748  
          * @since 1.3
 749  
          */
 750  86
         public ConstructorDetails(Class type, ConstructorDetails parent, boolean isnull, boolean isgroup) {
 751  86
             _parent = parent;
 752  86
             _type = type;
 753  86
             _argTypes = new ArrayList();
 754  86
             _argValues = new ArrayList();
 755  86
             _isnull = isnull;
 756  86
             _isgroup = isgroup;
 757  86
         }
 758  
 
 759  
         public void addArgument(Object value) {
 760  0
             addArgument(value.getClass(),value);
 761  0
         }
 762  
 
 763  
         public void addArgument(Class type, Object val) {
 764  232
             if(_isnull) {
 765  0
                 throw new NullPointerException("can't add arguments to null instances");
 766  
             }
 767  232
             _argTypes.add(type);
 768  232
             _argValues.add(val);
 769  232
         }
 770  
 
 771  
         public Class getType() {
 772  128
             return _type;
 773  
         }
 774  
 
 775  
         public ConstructorDetails getParent() {
 776  86
             return _parent;
 777  
         }
 778  
 
 779  
         public Object createObject() throws InstantiationException, IllegalAccessException, InvocationTargetException {
 780  86
             if(_isnull) {
 781  26
                 return null;
 782  60
             } else if( _isgroup ) {
 783  18
                 if (_type.equals(Object[].class)) {
 784  4
                     return _argValues.toArray();
 785  14
                 } else if (_type.equals(Collection.class) || _type.equals(List.class)) {
 786  14
                     return _argValues;
 787  
                 } else {
 788  0
                     throw new IllegalStateException("implementation error: unhandled _type:" + _type);
 789  
                 }
 790  
             } else {
 791  42
                 Class k = getType();
 792  42
                 Class[] argtypes = (Class[])_argTypes.toArray(new Class[0]);
 793  42
                 Object[] argvals = _argValues.toArray();
 794  42
                 return ConstructorUtil.invokeConstructor(k,argtypes,argvals);
 795  
             }
 796  
         }
 797  
     }
 798  
 
 799  
 }