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 }