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> <object class="java.util.Date"/></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><object></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> <object class="java.util.Date"/>
075 * <object class="java.util.Date"/></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><object></tt> elements in the JOCL fragment.
081 * <p>
082 * As we've seen, when used with no child-elements, the <tt><object></tt>
083 * tag will cause the no-argument constructor of the specified class to be invoked.
084 * It is also possible to nest <tt><object></tt> tags to provide arguments
085 * for the constructor.
086 * For example, the fragment:
087 * <pre> <object class="mypackage.Foo">
088 * <object class="mypackage.Bar"/>
089 * </object></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> <byte value="3"/>
097 * <boolean value="false"/>
098 * <char value="c"/>
099 * <double value="3.14159"/>
100 * <float value="3.14"/>
101 * <int value="17"/>
102 * <long value="1700000"/>
103 * <short value="1"/>
104 * <string value="The quick brown fox..."/></pre>
105 * <p>
106 * When invoked at the "root" level (that is, with no <tt><object></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><object></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> <int value="5"/>
114 * <int value="26"/>
115 * <int value="100"/></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> <int value="5"/>
121 * <int value="26"/>
122 * <int value="100"/></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> <object class="mypackage.Foo"/>
130 * <object class="java.lang.Integer"><int value="5"/></object>
131 * <object class="java.lang.Integer"><int value="26"/></object>
132 * <object class="java.lang.Integer"><int value="100"/></object>
133 * </object></pre>
134 * <p>
135 * If you want to invoke the <tt>mypackage.Foo(int,int,int)</tt>
136 * constructor, use:
137 * <pre> <object class="mypackage.Foo"/>
138 * <int value="5"/>
139 * <int value="26"/>
140 * <int value="100"/>
141 * </object></pre>
142 * <p>
143 * If you'd like to creat a <tt>null</tt> object, use:
144 * <pre> <object class="mypackage.Bar" null="true"/></pre>
145 * <p>
146 * Here's a simple but complete example:
147 * <pre> <?xml version="1.0"?>
148 * <arbitrary-root xmlns="http://apache.org/xml/xmlns/jakarta/commons/jocl">
149 * <string value="Hello World!"/>
150 * <string/>
151 * <boolean/>
152 * <boolean value="true"/>
153 * <byte value="1"/>
154 * <short value="1"/>
155 * <int value="1"/>
156 * <long value="1"/>
157 * <float value="1.0"/>
158 * <double value="1.0"/>
159 * <object class="java.util.Date"/>
160 * <object class="java.util.Date">
161 * <int value="1"/>
162 * <int value="1"/>
163 * <int value="1"/>
164 * </object>
165 * </arbitrary-root></pre>
166 * <p>
167 * Formally, a DTD for the JOCL grammar is as follows:
168 * <p>
169 * <pre>
170 * <!ELEMENT object (object|array|collection|list|byte|boolean|char|double|float|int|long|short|string)*>
171 * <!ATTLIST object
172 * class CDATA #REQUIRED
173 * null (true|false) "false">
174 *
175 * <!ELEMENT byte EMPTY>
176 * <!ATTLIST byte value CDATA #REQUIRED>
177 *
178 * <!ELEMENT boolean EMPTY>
179 * <!ATTLIST boolean value (true|false) #REQUIRED>
180 *
181 * <!ELEMENT char EMPTY>
182 * <!ATTLIST char value CDATA #REQUIRED>
183 *
184 * <!ELEMENT double EMPTY>
185 * <!ATTLIST double value CDATA #REQUIRED>
186 *
187 * <!ELEMENT float EMPTY>
188 * <!ATTLIST float value CDATA #REQUIRED>
189 *
190 * <!ELEMENT int EMPTY>
191 * <!ATTLIST int value CDATA #REQUIRED>
192 *
193 * <!ELEMENT long EMPTY>
194 * <!ATTLIST long value CDATA #REQUIRED>
195 *
196 * <!ELEMENT short EMPTY>
197 * <!ATTLIST short value CDATA #REQUIRED>
198 *
199 * <!ELEMENT string EMPTY>
200 * <!ATTLIST string value CDATA #REQUIRED>
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 }