You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by js...@apache.org on 2002/03/04 04:15:54 UTC

cvs commit: jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/digester/rss Channel.betwixt

jstrachan    02/03/03 19:15:54

  Modified:    betwixt  OVERVIEW.html STATUS.html build.xml
               betwixt/src/java/org/apache/commons/betwixt
                        NodeDescriptor.java XMLIntrospector.java
               betwixt/src/java/org/apache/commons/betwixt/digester
                        AddDefaultsRule.java ElementRule.java
                        XMLBeanInfoDigester.java XMLIntrospectorHelper.java
               betwixt/src/java/org/apache/commons/betwixt/expression
                        MethodUpdater.java
               betwixt/src/java/org/apache/commons/betwixt/io
                        BeanCreateRule.java BeanReader.java BeanWriter.java
               betwixt/src/test/org/apache/commons/betwixt
                        RSSBeanReader.java TestRSSRoundTrip.java
                        rss-example.xml
               betwixt/src/test/org/apache/commons/digester/rss
                        Channel.betwixt
  Added:       betwixt  RELEASE-NOTES.txt
  Removed:     betwixt  TODO.txt
  Log:
  Patched Betwixt to be able to properly default Digester rules auto-detecting addFoo(Foo) methods. This means that Betwixt can now bidirectionally turn the RSS example beans into XML and parse it back into beans. I've added round tripping JUnit test cases to ensure that Betwixt behaves like the RSSDigester example as well as that round tripping to XML then beans doesn't loose any information. Also updated the documentation somewhat to describe the addFoo() method naming conventions. Also removed the TODO.txt as the updated STATUS.html now contains a complete list of things to do. Finally some release notes have been added to CVS.
  
  Revision  Changes    Path
  1.3       +134 -12   jakarta-commons-sandbox/betwixt/OVERVIEW.html
  
  Index: OVERVIEW.html
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/OVERVIEW.html,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- OVERVIEW.html	19 Feb 2002 06:10:26 -0000	1.2
  +++ OVERVIEW.html	4 Mar 2002 03:15:53 -0000	1.3
  @@ -11,11 +11,13 @@
   <body>
   
   <h1>Jakarta Commons <i>Betwixt</i> Overview</h1>
  -<p>Betwixt aims to be a Java Bean to XML mapping library. It provides an 
  -XMLIntrospector in a similar manner to the Introspector in the java.beans 
  +<p>Betwixt is a library which maps Java Beans to XML. It provides an XMLIntrospector in a similar manner to the Introspector in the java.beans 
   package which defines how a bean appears as XML. There are many ways of encoding 
  -beans as XML. Betwixt will provide a default representation based on 
  -introspection which can be customized to taste to get nicer looking XML.</p>
  +beans as XML. Betwixt  provides a default representation based on 
  +introspection which can be customized to taste to get nicer looking XML or to 
  +map your beans to some external XML schema. In addition to the mapping API 
  +Betwixt also provides objects to turn beans into XML and to parse XML and turn 
  +it into beans. Namely BeanReader and BeanWriter.</p>
   <h2>Getting Started</h2>
   <p>Probably the best way to get started is to look at some examples. The best 
   example to start with is the Ant target &quot;demo.rss&quot; which runs the RSSBeanWriter 
  @@ -27,11 +29,54 @@
   create a Channel bean and then write it out again as XML using the default 
   XMLIntrospector and the BeanWriter. You should see the XML come out from the 
   Channel bean which looks similar to a real RSS document.</p>
  +<p>The next example to look at is</p>
  +<pre>ant demo-rss2</pre>
  +<p>This is similar to the above but uses a BeanReader to parse the RSS file. So 
  +this is Betwixt defaulting the Digster rules required to parse the XML document. 
  +Then the BeanWriter is used to output the beans that get created.</p>
  +<h2>Mapping beans to XML</h2>
  +<p>There are various ways of mapping beans to an XML structure. For example 
  +consider a simple bean</p>
  +<pre>public class CustomerBean {
  +    public String getName();
  +    public Order[] getOrders();
  +    public String[] getEmailAddresses();
  +}</pre>
  +<p>This could be mapped to XML as these various ways</p>
  +<h3>Example 1</h3>
  +<p>This example uses attributes for primitive types.</p>
  +<pre>&lt;CustomerBean name=&quot;James&quot;&gt;
  +    &lt;order id=&quot;1&quot;&gt;...&lt;/order&gt;
  +    &lt;order id=&quot;2&quot;&gt;...&lt;/order&gt;
  +    &lt;emailAddress&gt;jstrachan@apache.org&lt;/emailAddress&gt;
  +&lt;/CustomerBean&gt;</pre>
  +<h3>Example 2</h3>
  +<p>This exmaple uses elements for all properties and wraps collections in an 
  +extra element (which can be quite common in XML schemas). Also note that some 
  +element names have been changed.</p>
  +<pre>&lt;customer&gt;
  +    &lt;name&gt;James&lt;/name&gt;
  +    &lt;orders&gt;
  +        &lt;order id=&quot;1&quot;&gt;...&lt;/order&gt;
  +        &lt;order id=&quot;2&quot;&gt;...&lt;/order&gt;
  +    &lt;/orders&gt;
  +    &lt;email-addresses&gt;
  +        &lt;email-address&gt;jstrachan@apache.org&lt;/email-address&gt;
  +    &lt;/email-addresses&gt;
  +&lt;/customer&gt;    </pre>
  +<p>Betixt aims to provide a diversity of possible mappings such that the 
  +developer can choose, if they wish, how their beans appear as XML to support 
  +whatever XML encoding scheme that is desired. If no particular mapping is 
  +provided then Betwixt will create a default mapping for you. Also the 
  +customization mechanism allows you to just override the parts you want to and 
  +let Betwixt default the rest. So if you just want to rename a property in XML 
  +for a certain type, thats all you need to do. No need to hand-code what Betwixt 
  +can deduce for itself. </p>
   <h2>Customizing the mapping of a bean to XML</h2>
   
   <p>The XMLIntrospector will look for files of the form <i>className.betwixt</i> 
   on the classpath using the same ClassLoader used to load the given class and use 
  -that document to specify a types mapping to XML. If this file does not exist 
  +that document to specify the mapping to XML. If this file does not exist 
   then the default introspection rules are used.</p>
   <p>The simplest possible file may just set the name of the element. e.g.</p>
   
  @@ -41,13 +86,15 @@
     &lt;addDefaults/&gt;
   &lt;/info&gt;</pre>
   
  -<p>The above means to use the name 'foo' for the outer most element for the 
  +<p>The above means to use the name 'channel' for the outer most element for the 
   given type. The &lt;addDefaults&gt; means to add the defaults from the introspector. 
   This allows you to just rename a few properties then let the introspector do the 
   rest. There is also a &lt;hide&gt; element which allows one or more properties to be 
  -hidden. Also note that the &lt;element&gt; and &lt;attribute&gt; tags can be hidden to any 
  +hidden. Also note that the &lt;element&gt; and &lt;attribute&gt; tags can be 
  +nested to any 
   kind of depth allowing whatever XML structure you wish. This can be useful if 
  -you wish to wrap collections in some arbitrary collectino tags e.g.</p>
  +you wish to wrap collections in some arbitrary collection tags or to group 
  +properties of a bean together in some XML structure. e.g.</p>
   
   <pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
   &lt;info primitiveTypes=&quot;attribute&quot;&gt;
  @@ -56,6 +103,7 @@
         &lt;element name=&quot;customer&quot; property=&quot;customers&quot;/&gt;
       &lt;/element&gt;
       &lt;element name=&quot;foo&quot;&gt;
  +      &lt;attribute name=&quot;qqq&quot; property=&quot;ppp&quot;/&gt;
         &lt;element name=&quot;bar&quot; property=&quot;xyz&quot;/&gt;
       &lt;hide property=&quot;something&quot;/&gt;
       &lt;addDefaults/&gt;
  @@ -67,7 +115,8 @@
   used to specify whether primitive java types (strings, numbers, dates etc) are 
   specified as attributes or elements by default.</p>
   <p>Finally static text can be specified using a value attribute inside an 
  -&lt;element&gt; or &lt;attribute&gt; tag. e.g. to add a version label to the 
  +&lt;element&gt; or &lt;attribute&gt; tag. e.g. to add constant attributes such 
  +as a version label to the 
   generated XML...</p>
   
   <pre>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;
  @@ -80,9 +129,82 @@
   &lt;/info&gt;
   </pre>
   
  -<p>&nbsp;</p>
  -<p>&nbsp;</p>
  -<p>&nbsp;</p>
  +<h2>Bean naming conventions</h2>
  +
  +<p>The Java Beans specification contains various naming conventions that should 
  +be used when writing beans that will allow the beans introspector to 
  +automatically guess the properties in a bean and their getters &amp; setter methods 
  +etc. Betwixt will use these same naming conventions to deduce how to make the 
  +beans appear as XML. There are some other naming conventions that you can use to 
  +make your beans easier to output as XML or parse.</p>
  +
  +<h3><b>Adder methods matching getter methods for composite properties</b></h3>
  +<p>To use: create an add method to match the getter method for 'composite 
  +properties'.</p>
  +<pre>public class SomeBean {
  +    public &lt;CollectionType&gt; getFoo*();
  +    public void addFoo(&lt;SingularType&gt; foo);
  +}</pre>
  +<p>Where CollectionType can be an array, a Collection, Enumeration, Iterator, 
  +Map. The [SinglularType] refers to the type of an item in the collection. The 
  +name of the getter property starts with 'Foo'. So 'Foo' is the singular name, 
  +the plural collection name could be Foos, FooArray, FooList, FooIterator or some 
  +other encoding, though the plural name should start with the singular name for 
  +auto-detection to work properly.</p>
  +<h4>Examples</h4>
  +<p>In the RSS example from Digester there's a bean which matches this pattern.</p>
  +<pre>
  +public class Channel
  +
  +    public Item[] getItems();
  +
  +    public void addItem(Item item);
  +}
  +</pre>
  +<p>This means that the following bean does not match this naming convention, 
  +since the plural property name does not start with the singular name..</p>
  +<pre>public class Foo {
  +    public Collection getPeople();
  +    public void addPerson(Person person);
  +}</pre>
  +<p>Though these two beans do match</p>
  +<pre>public class Foo {
  +    public Collection getPersonCollection();
  +    public void addPerson(Person person);
  +}
  +public class Foo {
  +    public Iterator getPersonIterator();
  +    public void addPerson(Person person);
  +}
  +</pre>
  +<p>The following are other valid examples of <i>composite-getter</i> methods and 
  +their matching <i>adder</i> methods.</p>
  +<table border="1" cellpadding="0" cellspacing="0" style="border-collapse: collapse" bordercolor="#111111" width="100%">
  +  <tr>
  +    <td width="50%" align="center"><b>Composite getter method</b></td>
  +    <td width="50%" align="center"><b>Adder method</b></td>
  +  </tr>
  +  <tr>
  +    <td width="50%" align="center">getChildren()</td>
  +    <td width="50%" align="center">addChild()</td>
  +  </tr>
  +  <tr>
  +    <td width="50%" align="center">getPersonList()</td>
  +    <td width="50%" align="center">addPerson()</td>
  +  </tr>
  +  <tr>
  +    <td width="50%" align="center">getItems()</td>
  +    <td width="50%" align="center">addItem()</td>
  +  </tr>
  +  <tr>
  +    <td width="50%" align="center">getChannels()</td>
  +    <td width="50%" align="center">addChannel()</td>
  +  </tr>
  +  <tr>
  +    <td width="50%" align="center">getSheep()</td>
  +    <td width="50%" align="center">addSheep()</td>
  +  </tr>
  +</table>
   
   </body>
   
  
  
  
  1.5       +35 -13    jakarta-commons-sandbox/betwixt/STATUS.html
  
  Index: STATUS.html
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/STATUS.html,v
  retrieving revision 1.4
  retrieving revision 1.5
  diff -u -r1.4 -r1.5
  --- STATUS.html	21 Feb 2002 01:19:08 -0000	1.4
  +++ STATUS.html	4 Mar 2002 03:15:53 -0000	1.5
  @@ -7,7 +7,7 @@
   
   <div align="center">
   <h1>The Jakarta Commons <em>Betwixt</em> Package</h1>
  -$Id: STATUS.html,v 1.4 2002/02/21 01:19:08 jstrachan Exp $<br>
  +$Id: STATUS.html,v 1.5 2002/03/04 03:15:53 jstrachan Exp $<br>
   <a href="#Introduction">[Introduction]</a>
   <a href="#Dependencies">[Dependencies]</a>
   <a href="#Release Info">[Release Info]</a>
  @@ -86,6 +86,37 @@
     </tr>
   
     <tr>
  +    <td><b>Make ID generation optional</b>. We should make ID generation 
  +    optional - either at the BeanWriter level or maybe via the XMLBeanInfo 
  +    level. Also it would be nice to turn the ID generator code into an 
  +    Expression which can be integrated into the default NodeDescriptors - so 
  +    that writers of bean XML do not need to understand anything about ID 
  +    generation.</td>
  +    <td align="center">&nbsp;</td>
  +  </tr>
  +
  +  <tr>
  +    <td><b>Support hiding of empty elements</b>. Right now XML output contains 
  +    many empty elements for missing properties. It would be nice to allow empty 
  +    properties to be hidden by default (or turned back on again via the betwixt 
  +    XML config files).</td>
  +    <td align="center">&nbsp;</td>
  +  </tr>
  +
  +  <tr>
  +    <td><b>Improve digester defaulting</b>. We should try improve the automatic 
  +    creation of Digester rules by the BeanReader class.  
  +    <p>Right now we deduce all addFoo(Foo foo) methods 
  +    in the default digester. Though we should also support indexed properties as well via 
  +    the setFoo(Foo foo, int index). Also setters which take Arrays should be 
  +    supported somehow.<p>Also right now there is no way to configure the 
  +    addMethods in the betwixt XML config - the default introspector seaches for 
  +    methods called add* which take one argument. Maybe adding an &lt;addMethod&gt; to 
  +    the betwixt config might help. </td>
  +    <td align="center">&nbsp;</td>
  +  </tr>
  +
  +  <tr>
       <td><strong>More complex mappings</strong>. Demonstrate the use of Betwixt 
       with complex Java Bean graphs.
       <p>For example the betwixt file could allow methods to be used in addition 
  @@ -118,24 +149,15 @@
     </tr>
   
     <tr>
  -    <td><b>Improve digester defaulting</b>. We should try improve the automatic 
  -    creation of Digester rules by the BeanReader class. Maybe adding some 
  -    configuration details in the betwixt XML document, say an <i>addMethod=&quot;addFoo&quot;</i> 
  -    attribute or something. We should maybe deduce all addFoo(Foo foo) methods 
  -    in the default digester.</td>
  +    <td><b>Consider DOM implementation</b>. This would allow Betwixt to be used 
  +    to generate a DOM objects that can be fed into XSLT or TrAX engines to 
  +    transform beans into other markup representations like HTML</td>
       <td align="center">&nbsp;</td>
     </tr>
   
     <tr>
       <td><strong>Additional Documentation</strong>.  Create simple
           User's Guide, examples, or other documentation for this package.</td>
  -    <td align="center">&nbsp;</td>
  -  </tr>
  -
  -  <tr>
  -    <td><b>Online javadoc</b>. Somehow it'd be good to have the javadoc online 
  -    somewhere, generated from the source. Either via Craig's nightly build or 
  -    the Gump build might be a good idea</td>
       <td align="center">&nbsp;</td>
     </tr>
   
  
  
  
  1.14      +5 -15     jakarta-commons-sandbox/betwixt/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/build.xml,v
  retrieving revision 1.13
  retrieving revision 1.14
  diff -u -r1.13 -r1.14
  --- build.xml	19 Feb 2002 15:31:30 -0000	1.13
  +++ build.xml	4 Mar 2002 03:15:53 -0000	1.14
  @@ -3,7 +3,7 @@
   
   <!--
           "Digester" component of the Jakarta Commons Subproject
  -        $Id: build.xml,v 1.13 2002/02/19 15:31:30 jstrachan Exp $
  +        $Id: build.xml,v 1.14 2002/03/04 03:15:53 jstrachan Exp $
   -->
   
   
  @@ -217,6 +217,8 @@
                      href="http://jakarta.apache.org/commons/collections/api/"/>
         <link     offline="true" packagelistLoc="${commons-logging.api}"
                      href="http://jakarta.apache.org/commons/logging/api/"/>
  +      <link     offline="true" packagelistLoc="${commons-digester.api}"
  +                   href="http://jakarta.apache.org/commons/digester/api/"/>
         <link     offline="true"  packagelistLoc="${jdk.api}"
                      href="http://java.sun.com/products/jdk/1.3/docs/api"/>
         <link     offline="true"  packagelistLoc="${jaxp.api}"
  @@ -262,12 +264,10 @@
         description="Runs test of bean reader">
       <java classname="org.apache.commons.betwixt.TestBeanReader" fork="yes">
         <classpath refid="test.classpath"/>
  -        <!-- this all seems a bit much to get some logging? -->
  -      <sysproperty key="org.apache.commons.logging.log" value="org.apache.commons.logging.impl.SimpleLog"/>
  +      <!-- this all seems a bit much to get some logging? -->
         <sysproperty key="org.apache.commons.logging.simplelog.defaultlog" value="debug"/>
         <sysproperty key="org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester" value="warn"/>
         <sysproperty key="org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester.sax" value="warn"/>
  -      <sysproperty key="org.apache.commons.logging.simplelog.defaultlog" value="debug"/>
       </java>
      </target>
   
  @@ -275,12 +275,10 @@
         description="Runs the test of the XMLBeanInfo digester">
       <java classname="org.apache.commons.betwixt.TestXMLBeanInfoDigester" fork="yes">
         <classpath refid="test.classpath"/>
  -        <!-- this all seems a bit much to get some logging? maybe a properties file might help -->
  -      <sysproperty key="org.apache.commons.logging.log" value="org.apache.commons.logging.impl.SimpleLog"/>
  +      <!-- this all seems a bit much to get some logging? maybe a properties file might help -->
         <sysproperty key="org.apache.commons.logging.simplelog.defaultlog" value="debug"/>
         <sysproperty key="org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester" value="warn"/>
         <sysproperty key="org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester.sax" value="warn"/>
  -      <sysproperty key="org.apache.commons.logging.simplelog.defaultlog" value="debug"/>
       </java>
      </target>
   
  @@ -315,14 +313,6 @@
       <echo message="Running the RSS sample..."/>
       <java classname="org.apache.commons.betwixt.RSSBeanWriter" fork="yes">
         <classpath refid="test.classpath"/>
  -        <!-- this all seems a bit much to get some logging? maybe a properties file might help -->
  -<!--
  -      <sysproperty key="org.apache.commons.logging.impl.log" value="org.apache.commons.logging.impl.SimpleLog"/>
  --->
  -      <sysproperty key="org.apache.commons.logging.simplelog.defaultlog" value="debug"/>
  -      <sysproperty key="org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester" value="warn"/>
  -      <sysproperty key="org.apache.commons.logging.simplelog.log.org.apache.commons.digester.Digester.sax" value="warn"/>
  -      <sysproperty key="org.apache.commons.logging.simplelog.defaultlog" value="debug"/>
       </java>
      </target>
   
  
  
  
  1.1                  jakarta-commons-sandbox/betwixt/RELEASE-NOTES.txt
  
  Index: RELEASE-NOTES.txt
  ===================================================================
  $Id: RELEASE-NOTES.txt,v 1.1 2002/03/04 03:15:53 jstrachan Exp $
  
  			 Commons Betwixt Package
  			   Version 1.0-dev
  			    Release Notes
  
  
  INTRODUCTION:
  
  This document contains the release notes for this version of the Commons
  Betwixt package, and highlights changes since the previous version.  The
  current release adds new features and bug fixes, and is being done now to
  follow the release early/release often mentality.
  
  
  NEW FEATURES:
  
  * 
  
  
  BUG FIXES:
  
  
  
  
  
  1.7       +27 -5     jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/NodeDescriptor.java
  
  Index: NodeDescriptor.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/NodeDescriptor.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- NodeDescriptor.java	7 Feb 2002 19:17:26 -0000	1.6
  +++ NodeDescriptor.java	4 Mar 2002 03:15:54 -0000	1.7
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/NodeDescriptor.java,v 1.6 2002/02/07 19:17:26 jstrachan Exp $
  - * $Revision: 1.6 $
  - * $Date: 2002/02/07 19:17:26 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/NodeDescriptor.java,v 1.7 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.7 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,7 +57,7 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: NodeDescriptor.java,v 1.6 2002/02/07 19:17:26 jstrachan Exp $
  + * $Id: NodeDescriptor.java,v 1.7 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt;
   
  @@ -70,7 +70,7 @@
     * or they can have a local name, qualified name and a namespace uri.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.6 $
  +  * @version $Revision: 1.7 $
     */
   public class NodeDescriptor {
   
  @@ -87,6 +87,9 @@
       private String propertyName;
       /** the property type associated with this node, if any */
       private Class propertyType;
  +    /** the singular property type (i.e. the type ignoring the Collection or Array */
  +    private Class singularPropertyType;
  +    
       
       /** Base constructor */
       public NodeDescriptor() {
  @@ -191,4 +194,23 @@
       public void setPropertyName(String propertyName) {
           this.propertyName = propertyName;
       }
  +    
  +    /** 
  +     * @return if this property is a 1-N relationship then this returns the type
  +     * of a single property value.
  +     */
  +    public Class getSingularPropertyType() {
  +        if ( singularPropertyType == null ) {
  +            return getPropertyType();
  +        }
  +        return singularPropertyType;
  +    }
  +    
  +    /** 
  +     * Sets the singular property type (i.e. the type ignoring the Collection or Array
  +     */
  +    public void setSingularPropertyType(Class singularPropertyType) {
  +        this.singularPropertyType = singularPropertyType;
  +    }
  +
   }
  
  
  
  1.19      +28 -12    jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/XMLIntrospector.java
  
  Index: XMLIntrospector.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/XMLIntrospector.java,v
  retrieving revision 1.18
  retrieving revision 1.19
  diff -u -r1.18 -r1.19
  --- XMLIntrospector.java	25 Feb 2002 19:07:15 -0000	1.18
  +++ XMLIntrospector.java	4 Mar 2002 03:15:54 -0000	1.19
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/XMLIntrospector.java,v 1.18 2002/02/25 19:07:15 rdonkin Exp $
  - * $Revision: 1.18 $
  - * $Date: 2002/02/25 19:07:15 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/XMLIntrospector.java,v 1.19 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.19 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,7 +57,7 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: XMLIntrospector.java,v 1.18 2002/02/25 19:07:15 rdonkin Exp $
  + * $Id: XMLIntrospector.java,v 1.19 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt;
   
  @@ -90,6 +90,7 @@
   import org.apache.commons.betwixt.expression.MethodUpdater;
   import org.apache.commons.betwixt.expression.StringExpression;
   import org.apache.commons.betwixt.digester.XMLBeanInfoDigester;
  +import org.apache.commons.betwixt.digester.XMLIntrospectorHelper;
   
   /** <p><code>XMLIntrospector</code> an introspector of beans to create a XMLBeanInfo instance.</p>
     *
  @@ -99,7 +100,7 @@
     * Later requests for the same class will return the cached value.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.18 $
  +  * @version $Revision: 1.19 $
     */
   public class XMLIntrospector {
   
  @@ -112,7 +113,7 @@
       /** Is <code>XMLBeanInfo</code> caching enabled? */
       boolean cachingEnabled = true;
       /** Digester used to parse the XML descriptor files */
  -    private XMLBeanInfoDigester digester = new XMLBeanInfoDigester();
  +    private XMLBeanInfoDigester digester;
       /** Base constructor */
       public XMLIntrospector() {
       }
  @@ -200,7 +201,7 @@
           XMLBeanInfo answer = createXMLBeanInfo( beanInfo );
   
           BeanDescriptor beanDescriptor = beanInfo.getBeanDescriptor();
  -        Class beanType = beanDescriptor.getBeanClass();
  +        Class beanClass = beanDescriptor.getBeanClass();
           
           ElementDescriptor elementDescriptor = new ElementDescriptor();
           elementDescriptor.setLocalName( beanDescriptor.getName() );
  @@ -209,16 +210,16 @@
           log.trace(elementDescriptor);
           
           // add default string value for primitive types
  -        if ( isPrimitiveType( beanType ) ) {
  +        if ( isPrimitiveType( beanClass ) ) {
               elementDescriptor.setTextExpression( StringExpression.getInstance() );
               elementDescriptor.setPrimitiveType(true);
           }
  -        else if ( isLoopType( beanType ) ) {
  +        else if ( isLoopType( beanClass ) ) {
               ElementDescriptor loopDescriptor = new ElementDescriptor();
               loopDescriptor.setContextExpression(
                   new IteratorExpression( EmptyExpression.getInstance() )
               );
  -            if ( Map.class.isAssignableFrom( beanType ) ) {
  +            if ( Map.class.isAssignableFrom( beanClass ) ) {
                   loopDescriptor.setQualifiedName( "entry" );
               }
               elementDescriptor.setElementDescriptors( new ElementDescriptor[] { loopDescriptor } );
  @@ -258,6 +259,10 @@
           }
           
           answer.setElementDescriptor( elementDescriptor );        
  +        
  +        // default any addProperty() methods
  +        XMLIntrospectorHelper.defaultAddMethods( elementDescriptor, beanClass );
  +        
           return answer;
       }
       
  @@ -299,6 +304,10 @@
                   }
                   // synchronized method so this digester is only used by
                   // one thread at once
  +                if ( digester == null ) {
  +                    digester = new XMLBeanInfoDigester();
  +                    digester.setXMLIntrospector( this );
  +                }
                   digester.setBeanClass( aClass );
                   return (XMLBeanInfo) digester.parse( urlText );
               }
  @@ -372,7 +381,10 @@
                   elements.add( nodeDescriptor );
               }
               nodeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
  -            nodeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +            
  +            if ( writeMethod != null ) {
  +                nodeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +            }
           }
           else if ( isLoopType( type ) ) {
               log.trace("Loop type");
  @@ -396,12 +408,16 @@
               log.trace( "Standard property" );
               ElementDescriptor elementDescriptor = new ElementDescriptor();
               elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
  -            elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +            
  +            if ( writeMethod != null ) {
  +                elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +            }
               
               nodeDescriptor = elementDescriptor;            
               elements.add( nodeDescriptor );
           }
           nodeDescriptor.setLocalName( propertyDescriptor.getName() );
  +        nodeDescriptor.setPropertyName( propertyDescriptor.getName() );
           nodeDescriptor.setPropertyType( type );        
           
           // XXX: associate more bean information with the descriptor?
  
  
  
  1.3       +22 -5     jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/AddDefaultsRule.java
  
  Index: AddDefaultsRule.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/AddDefaultsRule.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- AddDefaultsRule.java	19 Feb 2002 15:31:30 -0000	1.2
  +++ AddDefaultsRule.java	4 Mar 2002 03:15:54 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/AddDefaultsRule.java,v 1.2 2002/02/19 15:31:30 jstrachan Exp $
  - * $Revision: 1.2 $
  - * $Date: 2002/02/19 15:31:30 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/AddDefaultsRule.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,7 +57,7 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: AddDefaultsRule.java,v 1.2 2002/02/19 15:31:30 jstrachan Exp $
  + * $Id: AddDefaultsRule.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.digester;
   
  @@ -87,7 +87,7 @@
     * to the current element.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.2 $
  +  * @version $Revision: 1.3 $
     */
   public class AddDefaultsRule extends RuleSupport {
   
  @@ -136,6 +136,9 @@
                   log.info( "Caught introspection exception", e );
               }
           }
  +        
  +        // default any addProperty() methods
  +        XMLIntrospectorHelper.defaultAddMethods( getRootElementDescriptor(), beanClass );
       }
   
   
  @@ -176,4 +179,18 @@
               throw new SAXException( "Invalid use of <addDefaults>. It should be nested inside <element> element" );
           }            
       }     
  +    
  +    protected ElementDescriptor getRootElementDescriptor() {
  +        Object top = digester.peek();
  +        if ( top instanceof XMLBeanInfo ) {
  +            XMLBeanInfo beanInfo = (XMLBeanInfo) top;
  +            return beanInfo.getElementDescriptor();
  +        }
  +        else if ( top instanceof ElementDescriptor ) {
  +            ElementDescriptor parent = (ElementDescriptor) top;
  +            // XXX: could maybe walk up the parent hierarchy?
  +            return parent;
  +        }
  +        return null;
  +    }
   }
  
  
  
  1.3       +44 -14    jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/ElementRule.java
  
  Index: ElementRule.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/ElementRule.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- ElementRule.java	19 Feb 2002 06:10:26 -0000	1.2
  +++ ElementRule.java	4 Mar 2002 03:15:54 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/ElementRule.java,v 1.2 2002/02/19 06:10:26 jstrachan Exp $
  - * $Revision: 1.2 $
  - * $Date: 2002/02/19 06:10:26 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/ElementRule.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,11 +57,12 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: ElementRule.java,v 1.2 2002/02/19 06:10:26 jstrachan Exp $
  + * $Id: ElementRule.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.digester;
   
   import java.beans.BeanInfo;
  +import java.beans.IntrospectionException;
   import java.beans.Introspector;
   import java.beans.PropertyDescriptor;
   import java.lang.reflect.Method;
  @@ -88,7 +89,7 @@
   /** <p><code>ElementRule</code> the digester Rule for parsing the &lt;element&gt; elements.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.2 $
  +  * @version $Revision: 1.3 $
     */
   public class ElementRule extends RuleSupport {
   
  @@ -125,7 +126,13 @@
           
           String propertyName = attributes.getValue( "property" );
           descriptor.setPropertyName( propertyName );
  -        descriptor.setPropertyType( loadClass( attributes.getValue( "type" ) ) );
  +        
  +        descriptor.setPropertyType( 
  +            getPropertyType( attributes.getValue( "type" ), beanClass, propertyName ) 
  +        );
  +        
  +        // set the property type using reflection
  +        
           
           if ( propertyName != null && propertyName.length() > 0 ) {
               configureDescriptor(descriptor);
  @@ -165,15 +172,22 @@
       
       // Implementation methods
       //-------------------------------------------------------------------------    
  -    protected Class loadClass( String name ) {
  +    protected Class getPropertyType( String propertyClassName, Class beanClass, String propertyName ) {
           // XXX: should use a ClassLoader to handle complex class loading situations
  -        if ( name != null ) {
  +        if ( propertyClassName != null ) {
               try {
  -                return classLoader.loadClass(name);
  +                Class answer = classLoader.loadClass(propertyClassName);
  +                if (answer != null) {
  +                    return answer;
  +                }
               }
               catch (Exception e) {
               }
           }
  +        PropertyDescriptor descriptor = getPropertyDescriptor( beanClass, propertyName );        
  +        if ( descriptor != null ) { 
  +            return descriptor.getPropertyType();
  +        }
           return null;            
       }
       
  @@ -182,23 +196,39 @@
           Class beanClass = getBeanClass();
           if ( beanClass != null ) {
               String name = elementDescriptor.getPropertyName();
  +            PropertyDescriptor descriptor = getPropertyDescriptor( beanClass, name );
  +            if ( descriptor != null ) { 
  +                XMLIntrospectorHelper.configureProperty( elementDescriptor, descriptor );
  +                getProcessedPropertyNameSet().add( name );
  +            }
  +        }
  +    }    
  +
  +    /** 
  +     * Returns the property descriptor for the class and property name.
  +     * Note that some caching could be used to improve performance of this method.
  +     * Or this method could be added to PropertyUtils.
  +     */
  +    protected PropertyDescriptor getPropertyDescriptor( Class beanClass, String propertyName ) {
  +        if ( beanClass != null && propertyName != null ) {
               try {
                   BeanInfo beanInfo = Introspector.getBeanInfo( beanClass );
                   PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
                   if ( descriptors != null ) {
                       for ( int i = 0, size = descriptors.length; i < size; i++ ) {
                           PropertyDescriptor descriptor = descriptors[i];
  -                        if ( name.equals( descriptor.getName() ) ) {
  -                            XMLIntrospectorHelper.configureProperty( elementDescriptor, descriptor );
  -                            getProcessedPropertyNameSet().add( name );
  -                            break;
  +                        if ( propertyName.equals( descriptor.getName() ) ) {
  +                            return descriptor;
                           }
                       }
                   }
  +                return null;
               }
               catch (Exception e) {
                   log.warn( "Caught introspection exception", e );
               }
           }
  -    }    
  +        return null;
  +    }
  +    
   }
  
  
  
  1.3       +23 -5     jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLBeanInfoDigester.java
  
  Index: XMLBeanInfoDigester.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLBeanInfoDigester.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- XMLBeanInfoDigester.java	19 Feb 2002 06:10:26 -0000	1.2
  +++ XMLBeanInfoDigester.java	4 Mar 2002 03:15:54 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLBeanInfoDigester.java,v 1.2 2002/02/19 06:10:26 jstrachan Exp $
  - * $Revision: 1.2 $
  - * $Date: 2002/02/19 06:10:26 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLBeanInfoDigester.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,7 +57,7 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: XMLBeanInfoDigester.java,v 1.2 2002/02/19 06:10:26 jstrachan Exp $
  + * $Id: XMLBeanInfoDigester.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.digester;
   
  @@ -66,6 +66,8 @@
   
   import javax.xml.parsers.SAXParser;
   
  +import org.apache.commons.betwixt.XMLIntrospector;
  +
   import org.apache.commons.digester.Rule;
   import org.apache.commons.digester.Digester;
   
  @@ -78,7 +80,7 @@
     * containing XMLBeanInfo definitions for a JavaBean.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.2 $
  +  * @version $Revision: 1.3 $
     */
   public class XMLBeanInfoDigester extends Digester {
   
  @@ -93,6 +95,9 @@
       
       /** the set of property names processed so far */
       private Set processedPropertyNameSet = new HashSet();
  +
  +    /** the introspector that is using me */
  +    private XMLIntrospector introspector;
       
       /**
        * Construct a new XMLBeanInfoDigester with default properties.
  @@ -147,8 +152,21 @@
       /** Set whether attributes (or elements) should be used for primitive types. */
       public void setAttributesForPrimitives(boolean attributesForPrimitives) {
           this.attributesForPrimitives = attributesForPrimitives;
  +        if ( introspector != null ) {
  +            introspector.setAttributesForPrimitives( attributesForPrimitives );
  +        }
       }
   
  +    /** @return the introspector that is using me */
  +    public XMLIntrospector getXMLIntrospector() {
  +        return introspector;
  +    }
  +    
  +    /** Sets the introspector that is using me */
  +    public void setXMLIntrospector(XMLIntrospector introspector) {
  +        this.introspector = introspector;
  +    }
  +    
       // Implementation methods
       //-------------------------------------------------------------------------        
       protected void configure() {
  
  
  
  1.2       +111 -13   jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLIntrospectorHelper.java
  
  Index: XMLIntrospectorHelper.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLIntrospectorHelper.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- XMLIntrospectorHelper.java	19 Feb 2002 06:10:26 -0000	1.1
  +++ XMLIntrospectorHelper.java	4 Mar 2002 03:15:54 -0000	1.2
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLIntrospectorHelper.java,v 1.1 2002/02/19 06:10:26 jstrachan Exp $
  - * $Revision: 1.1 $
  - * $Date: 2002/02/19 06:10:26 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/digester/XMLIntrospectorHelper.java,v 1.2 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.2 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,7 +57,7 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: XMLIntrospectorHelper.java,v 1.1 2002/02/19 06:10:26 jstrachan Exp $
  + * $Id: XMLIntrospectorHelper.java,v 1.2 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.digester;
   
  @@ -97,7 +97,7 @@
     * common code shared between the digestor and introspector.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.1 $
  +  * @version $Revision: 1.2 $
     */
   public class XMLIntrospectorHelper {
   
  @@ -192,6 +192,7 @@
               nodeDescriptor = elementDescriptor;            
           }
           nodeDescriptor.setLocalName( propertyDescriptor.getName() );
  +        nodeDescriptor.setPropertyName( propertyDescriptor.getName() );
           nodeDescriptor.setPropertyType( type );        
           
           // XXX: associate more bean information with the descriptor?
  @@ -206,6 +207,13 @@
           Method readMethod = propertyDescriptor.getReadMethod();
           Method writeMethod = propertyDescriptor.getWriteMethod();
           
  +        elementDescriptor.setLocalName( propertyDescriptor.getName() );
  +        elementDescriptor.setPropertyType( type );        
  +        
  +        // XXX: associate more bean information with the descriptor?
  +        //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
  +        //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
  +        
           if ( readMethod == null ) {
               log.trace( "No read method" );
               return;
  @@ -224,7 +232,7 @@
           }
           if ( isPrimitiveType( type ) ) {
               elementDescriptor.setTextExpression( new MethodExpression( readMethod ) );
  -            elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +            elementDescriptor.setPrimitiveType(true);
           }
           else if ( isLoopType( type ) ) {
               log.trace("Loop type");
  @@ -234,18 +242,17 @@
               elementDescriptor.setContextExpression(
                   new IteratorExpression( new MethodExpression( readMethod ) )
               );
  +
  +            writeMethod = null;
           }
           else {
               log.trace( "Standard property" );
               elementDescriptor.setContextExpression( new MethodExpression( readMethod ) );
  -            elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );            
           }
  -        elementDescriptor.setLocalName( propertyDescriptor.getName() );
  -        elementDescriptor.setPropertyType( type );        
           
  -        // XXX: associate more bean information with the descriptor?
  -        //nodeDescriptor.setDisplayName( propertyDescriptor.getDisplayName() );
  -        //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
  +        if ( writeMethod != null ) {
  +            elementDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +        }
       }
       
       
  @@ -276,7 +283,10 @@
   
           log.trace( "Standard property" );
           attributeDescriptor.setTextExpression( new MethodExpression( readMethod ) );
  -        attributeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +        
  +        if ( writeMethod != null ) {
  +            attributeDescriptor.setUpdater( new MethodUpdater( writeMethod ) );
  +        }
           
           attributeDescriptor.setLocalName( propertyDescriptor.getName() );
           attributeDescriptor.setPropertyType( type );        
  @@ -286,6 +296,66 @@
           //nodeDescriptor.setShortDescription( propertyDescriptor.getShortDescription() );
       }
       
  +
  +    /** 
  +     * Add any addPropety(PropertyType) methods as Updaters 
  +     * which are often used for 1-N relationships in beans.
  +     * <br>
  +     * The tricky part here is finding which ElementDescriptor corresponds
  +     * to the method. e.g. a property 'items' might have an Element descriptor
  +     * which the method addItem() should match to. 
  +     * <br>
  +     * So the algorithm we'll use 
  +     * by default is to take the decapitalized name of the property being added
  +     * and find the first ElementDescriptor that matches the property starting with
  +     * the string. This should work for most use cases. 
  +     * e.g. addChild() would match the children property.
  +     */
  +    public static void defaultAddMethods( ElementDescriptor rootDescriptor, Class beanClass ) {
  +        // lets iterate over all methods looking for one of the form
  +        // add*(PropertyType)
  +        if ( beanClass != null ) {
  +            Method[] methods = beanClass.getMethods();
  +            for ( int i = 0, size = methods.length; i < size; i++ ) {
  +                Method method = methods[i];
  +                String name = method.getName();
  +                if ( name.startsWith( "add" ) ) {
  +                    // XXX: should we filter out non-void returning methods?
  +                    // some beans will return something as a helper
  +                    Class[] types = method.getParameterTypes();
  +                    if ( types != null && types.length == 1 ) {
  +                        String propertyName = Introspector.decapitalize( name.substring(3) );
  +
  +                        // now lets try find the ElementDescriptor which displays
  +                        // a property which starts with propertyName
  +                        // and if so, we'll set a new Updater on it if there
  +                        // is not one already
  +                        ElementDescriptor descriptor = findGetCollectionDescriptor( rootDescriptor, propertyName );
  +                        if ( descriptor != null ) {
  +                            if ( descriptor.getUpdater() == null ) {
  +                                if ( log.isDebugEnabled() ) {
  +                                    log.debug( 
  +                                        "Adding the updater for the method: " + method 
  +                                        + " on type: " + beanClass.getName() 
  +                                    );
  +                                }
  +                                descriptor.setUpdater( new MethodUpdater( method ) );
  +                                descriptor.setSingularPropertyType( types[0] );
  +                            }
  +                        }
  +                        else {
  +                            if ( log.isDebugEnabled() ) {
  +                                log.debug( 
  +                                    "Could not find an ElementDescriptor with property name: " 
  +                                    + propertyName + " to attach the add method: " + method 
  +                                );
  +                            }
  +                        }
  +                    }
  +                }
  +            }
  +        }
  +    }
       
       /** Returns true if the type is a loop type */
       public static boolean isLoopType(Class type) {
  @@ -307,4 +377,32 @@
               || type.isAssignableFrom( String.class ) 
               || type.isAssignableFrom( Date.class );
       }
  +    
  +    // Implementation methods
  +    //-------------------------------------------------------------------------    
  +    
  +    /** 
  +     * Attempts to find the element descriptor for the getter property that 
  +     * typically matches a collection or array. The property name is used
  +     * to match. e.g. if an addChild() method is detected the 
  +     * descriptor for the 'children' getter property should be returned.
  +     */
  +    protected static ElementDescriptor findGetCollectionDescriptor( ElementDescriptor rootDescriptor, String propertyName ) {
  +        ElementDescriptor[] children = rootDescriptor.getElementDescriptors();
  +        if ( children != null ) {
  +            for ( int i = 0, size = children.length; i < size; i++ ) {
  +                ElementDescriptor child = children[i];
  +                String childPropertyName = child.getPropertyName();
  +                if ( childPropertyName != null && childPropertyName.startsWith( propertyName ) ) {
  +                    return child;
  +                }
  +                ElementDescriptor answer = findGetCollectionDescriptor( child, propertyName );
  +                if ( answer != null ) {
  +                    return answer;
  +                }
  +            }
  +        }
  +        return null;
  +    }
  +    
   }
  
  
  
  1.4       +21 -6     jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/expression/MethodUpdater.java
  
  Index: MethodUpdater.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/expression/MethodUpdater.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- MethodUpdater.java	19 Feb 2002 06:10:27 -0000	1.3
  +++ MethodUpdater.java	4 Mar 2002 03:15:54 -0000	1.4
  @@ -5,12 +5,14 @@
    * version 1.1, a copy of which has been included with this distribution in
    * the LICENSE file.
    * 
  - * $Id: MethodUpdater.java,v 1.3 2002/02/19 06:10:27 jstrachan Exp $
  + * $Id: MethodUpdater.java,v 1.4 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.expression;
   
   import java.lang.reflect.Method;
   
  +import org.apache.commons.beanutils.ConvertUtils;
  +
   import org.apache.commons.logging.Log;
   import org.apache.commons.logging.LogFactory;
   
  @@ -19,7 +21,7 @@
     * or element.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.3 $
  +  * @version $Revision: 1.4 $
     */
   public class MethodUpdater implements Updater {
   
  @@ -28,6 +30,8 @@
       
       /** The method to call on the bean */
       private Method method;
  +    /** The type of the first parameter of the method */
  +    private Class valueType;
       
       /** Base constructor */
       public MethodUpdater() {
  @@ -35,15 +39,17 @@
       
       /** Convenience constructor sets method property */
       public MethodUpdater(Method method) {
  -        this.method = method;
  +        setMethod( method );
       }
   
       /** Updates the current bean context with the given String value */
       public void update(Context context, Object newValue) {
           Object bean = context.getBean();
           if ( bean != null ) {
  -            // XXX: should use the primitive type converters from 
  -            // commons-beanutils either BeanUtils or PropertyUtils
  +            if ( newValue instanceof String ) {
  +                // try to convert into primitive types
  +                newValue = ConvertUtils.convert( (String) newValue, valueType );
  +            }
               Object[] arguments = { newValue };
               try {
                   if ( log.isDebugEnabled() ) {
  @@ -65,6 +71,11 @@
       /** Sets the constant value of this expression */
       public void setMethod(Method method) {
           this.method = method;
  +        Class[] types = method.getParameterTypes();
  +        if ( types == null || types.length <= 0 ) {
  +            throw new IllegalArgumentException( "The Method must have at least one parameter" );
  +        }
  +        this.valueType = types[0];
       }
       
       // Implementation methods
  @@ -77,6 +88,10 @@
         */
       protected void handleException(Context context, Exception e) {
           // use the context's logger to log the problem
  -        context.getLog().error("[MethodExpression] Cannot evaluate expression", e);
  +        context.getLog().error(
  +            "[MethodUpdater] Cannot evaluate method: " + method 
  +            + " on bean: " + context.getBean() 
  +            + ". Exception: " + e, e 
  +        );
       }
   }
  
  
  
  1.4       +76 -29    jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanCreateRule.java
  
  Index: BeanCreateRule.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanCreateRule.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- BeanCreateRule.java	19 Feb 2002 06:10:27 -0000	1.3
  +++ BeanCreateRule.java	4 Mar 2002 03:15:54 -0000	1.4
  @@ -5,7 +5,7 @@
    * version 1.1, a copy of which has been included with this distribution in
    * the LICENSE file.
    * 
  - * $Id: BeanCreateRule.java,v 1.3 2002/02/19 06:10:27 jstrachan Exp $
  + * $Id: BeanCreateRule.java,v 1.4 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.io;
   
  @@ -19,6 +19,7 @@
   import org.apache.commons.betwixt.XMLIntrospector;
   import org.apache.commons.betwixt.expression.Context;
   import org.apache.commons.betwixt.expression.Updater;
  +import org.apache.commons.betwixt.digester.XMLIntrospectorHelper;
   
   import org.apache.commons.digester.Rule;
   import org.apache.commons.digester.Digester;
  @@ -32,7 +33,7 @@
     * from the betwixt XML metadata.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.3 $
  +  * @version $Revision: 1.4 $
     */
   public class BeanCreateRule extends Rule {
   
  @@ -47,17 +48,25 @@
       private boolean addedChildren;
       /** In this begin-end loop did we actually create a new bean */
       private boolean createdBean;
  +    /** The type of the bean to create */
  +    private Class beanClass;
  +    /** The prefix added to digester rules */
  +    private String pathPrefix;
       
  -    public BeanCreateRule(BeanReader digester, ElementDescriptor descriptor) {
  +    public BeanCreateRule(BeanReader digester, ElementDescriptor descriptor, Class beanClass) {
           super( digester );
           this.descriptor = descriptor;
           this.context = new Context();
  +        this.beanClass = beanClass;
  +        this.pathPrefix = descriptor.getQualifiedName() + "/";
       }
       
  -    public BeanCreateRule(BeanReader digester, ElementDescriptor descriptor, Context context) {
  +    public BeanCreateRule(BeanReader digester, ElementDescriptor descriptor, Context context, String pathPrefix) {
           super( digester );
           this.descriptor = descriptor;        
           this.context = context;
  +        this.beanClass = descriptor.getSingularPropertyType();
  +        this.pathPrefix = pathPrefix;
       }
       
           
  @@ -78,7 +87,7 @@
           createdBean = false;
           
           Object instance = null;
  -        if ( descriptor.getPropertyType() != null ) {
  +        if ( beanClass != null ) {
               instance = createBean(attributes);
               if ( instance != null ) {
                   createdBean = true;
  @@ -91,7 +100,6 @@
                       }
                       updater.update( context, instance );
                   }
  -                
                   context.setBean( instance );
                   
           
  @@ -113,7 +121,7 @@
                           );
   
                           updater = attributeDescriptor.getUpdater();
  -                        if ( updater != null ) {
  +                        if ( updater != null && value != null ) {
                               updater.update( context, value );
                           }
                       }
  @@ -145,10 +153,10 @@
       /** Factory method to create new bean instances */
       protected Object createBean(Attributes attributes) throws Exception {
           try {
  -            return descriptor.getPropertyType().newInstance();
  +            return beanClass.newInstance();
           }
           catch (Exception e) {
  -            log.debug( "Could not create instance of type: " + descriptor.getPropertyType().getName() );
  +            log.debug( "Could not create instance of type: " + beanClass.getName() );
               return null;
           }
       }
  @@ -157,36 +165,60 @@
       protected void addChildRules() {
           if ( ! addedChildren ) {
               addedChildren = true;
  -                        
  -            BeanReader digester = getBeanReader();            
  -            String prefix = descriptor.getQualifiedName() + "/";
               
  -            // if we are a reference to a type we should lookup the original
  -            // as this ElementDescriptor will be 'hollow' and have no child attributes/elements.
  -            // XXX: this should probably be done by the NodeDescriptors...
  -            ElementDescriptor typeDescriptor = getElementDescriptor( descriptor );
  -            //ElementDescriptor typeDescriptor = descriptor;
  -            
  -            ElementDescriptor[] childDescriptors = typeDescriptor.getElementDescriptors();
  -            if ( childDescriptors != null ) {
  -                
  -                for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
  -                    final ElementDescriptor childDescriptor = childDescriptors[i];
  +            addChildRules( pathPrefix, descriptor );
  +        }
  +    }
  +                        
  +    protected void addChildRules(String prefix, ElementDescriptor currentDescriptor) {
  +        BeanReader digester = getBeanReader();            
   
  -                    String path = prefix + childDescriptor.getQualifiedName();
  -                    if ( childDescriptor.isPrimitiveType() ) {
  -                        Rule rule = new Rule( digester ) {
  +        // if we are a reference to a type we should lookup the original
  +        // as this ElementDescriptor will be 'hollow' and have no child attributes/elements.
  +        // XXX: this should probably be done by the NodeDescriptors...
  +        ElementDescriptor typeDescriptor = getElementDescriptor( currentDescriptor );
  +        //ElementDescriptor typeDescriptor = descriptor;
  +
  +        ElementDescriptor[] childDescriptors = typeDescriptor.getElementDescriptors();
  +        if ( childDescriptors != null ) {
  +
  +            for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
  +                final ElementDescriptor childDescriptor = childDescriptors[i];
  +
  +                String propertyName = childDescriptor.getPropertyName();
  +                String qualifiedName = childDescriptor.getQualifiedName();
  +                if ( qualifiedName == null ) {
  +                    continue;
  +                }
  +                String path = prefix + qualifiedName;
  +                
  +                if ( propertyName != null ) {
  +                    Rule rule = null;
  +                    if ( isPrimitiveType( childDescriptor ) ) {
  +                        rule = new Rule( digester ) {
                               public void body(String text) throws Exception {
  +                                if ( log.isDebugEnabled() ) {
  +                                    log.debug( "Calling updater on bean: " + context.getBean() + " with text: " + text );
  +                                }
                                   childDescriptor.getUpdater().update( context, text );
                               }        
                           };
   
  -                        digester.addRule( path, rule );
                       }
                       else {
  -                        digester.addRule( path, new BeanCreateRule( digester, childDescriptor, context ) );
  +                        rule = new BeanCreateRule( digester, childDescriptor, context, path + "/" );
  +                    }                    
  +                    digester.addRule( path, rule );
  +                    
  +                    if ( log.isDebugEnabled() ) {
  +                        log.debug( "Added rule: " + rule + " to path: " + path );
                       }
                   }
  +
  +                ElementDescriptor[] grandChildren = childDescriptor.getElementDescriptors();
  +                if ( grandChildren != null && grandChildren.length > 0 ) {
  +                    addChildRules( path + "/", childDescriptor );
  +                }
               }
           }
       }
  @@ -200,7 +232,7 @@
        * This could be done automatically by the NodeDescriptors. Refer to TODO.txt for more info.
        */
       protected ElementDescriptor getElementDescriptor( ElementDescriptor propertyDescriptor ) {
  -        Class beanClass = propertyDescriptor.getPropertyType();
  +        Class beanClass = propertyDescriptor.getSingularPropertyType();
           if ( beanClass != null ) {
               XMLIntrospector introspector = getBeanReader().getXMLIntrospector();
               try {
  @@ -213,5 +245,20 @@
           }
           // could not find a better descriptor so use the one we've got
           return propertyDescriptor;
  +    }
  +    
  +    /** 
  +     * @return true if this descriptor is a primitive type or
  +     * the priperty descriptor is a collection of primitive types
  +     */
  +    protected boolean isPrimitiveType( ElementDescriptor descriptor ) {
  +        if ( descriptor.isPrimitiveType() ) {
  +            return true;
  +        }
  +        else {
  +            // maybe we are adding a primitve type to a collection/array
  +            Class beanClass = descriptor.getSingularPropertyType();
  +            return beanClass.isPrimitive() || XMLIntrospectorHelper.isPrimitiveType( beanClass );
  +        }
       }
   }
  
  
  
  1.4       +38 -20    jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanReader.java
  
  Index: BeanReader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanReader.java,v
  retrieving revision 1.3
  retrieving revision 1.4
  diff -u -r1.3 -r1.4
  --- BeanReader.java	19 Feb 2002 06:10:27 -0000	1.3
  +++ BeanReader.java	4 Mar 2002 03:15:54 -0000	1.4
  @@ -5,12 +5,14 @@
    * version 1.1, a copy of which has been included with this distribution in
    * the LICENSE file.
    * 
  - * $Id: BeanReader.java,v 1.3 2002/02/19 06:10:27 jstrachan Exp $
  + * $Id: BeanReader.java,v 1.4 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.io;
   
   import java.beans.IntrospectionException;
  +import java.util.HashSet;
   import java.util.Iterator;
  +import java.util.Set;
   
   import javax.xml.parsers.SAXParser;
   
  @@ -32,7 +34,7 @@
   /** <p><code>BeanReader</code> reads a tree of beans from an XML document.</p>
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.3 $
  +  * @version $Revision: 1.4 $
     */
   public class BeanReader extends Digester {
   
  @@ -40,6 +42,8 @@
       private XMLIntrospector introspector = new XMLIntrospector();    
       /** Log used for logging (Doh!) */
       private Log log = LogFactory.getLog( BeanReader.class.getName() );
  +    /** The registered classes */
  +    private Set registeredClasses = new HashSet();
       
       /**
        * Construct a new BeanReader with default properties.
  @@ -72,28 +76,30 @@
       
       /** Registers a bean class for use by the writer */
       public void registerBeanClass(Class beanClass) throws IntrospectionException {
  -        // introspect and find the ElementDescriptor to use as the root
  -        XMLBeanInfo xmlInfo = introspector.introspect( beanClass );
  -        ElementDescriptor elementDescriptor = xmlInfo.getElementDescriptor();        
  -        
  -        String path = elementDescriptor.getQualifiedName();
  -        
  -        Rule rule = new BeanCreateRule( this, elementDescriptor );
  -        addRule( path, rule );
  -        
  -        log.info( "Added rule: " + rule + " to path: " + path );
  +        if ( ! registeredClasses.contains( beanClass ) ) {
  +            registeredClasses.add( beanClass );
  +            
  +            // introspect and find the ElementDescriptor to use as the root
  +            XMLBeanInfo xmlInfo = introspector.introspect( beanClass );
  +            ElementDescriptor elementDescriptor = xmlInfo.getElementDescriptor();        
  +
  +            String path = elementDescriptor.getQualifiedName();
  +
  +            addBeanCreateRule( path, elementDescriptor, beanClass );
  +        }
       }
       
       /** Registers a bean class for use by the writer at the given path */
       public void registerBeanClass(String path, Class beanClass) throws IntrospectionException {
  -        // introspect and find the ElementDescriptor to use as the root
  -        XMLBeanInfo xmlInfo = introspector.introspect( beanClass );
  -        ElementDescriptor elementDescriptor = xmlInfo.getElementDescriptor();        
  -        
  -        Rule rule = new BeanCreateRule( this, elementDescriptor );
  -        addRule( path, rule );
  -        
  -        log.info( "Added rule: " + rule + " to path: " + path );
  +        if ( ! registeredClasses.contains( beanClass ) ) {
  +            registeredClasses.add( beanClass );
  +            
  +            // introspect and find the ElementDescriptor to use as the root
  +            XMLBeanInfo xmlInfo = introspector.introspect( beanClass );
  +            ElementDescriptor elementDescriptor = xmlInfo.getElementDescriptor();        
  +
  +            addBeanCreateRule( path, elementDescriptor, beanClass );
  +        }
       }
       
       // Properties
  @@ -143,4 +149,16 @@
       // Implementation methods
       //-------------------------------------------------------------------------    
       
  +    /** 
  +     * Adds a new bean create rule for the specified path
  +     */
  +    protected void addBeanCreateRule( String path, ElementDescriptor elementDescriptor, Class beanClass ) {
  +        Rule rule = new BeanCreateRule( this, elementDescriptor, beanClass );
  +        addRule( path, rule );
  +
  +        if ( log.isDebugEnabled() ) {
  +            log.debug( "Added rule: " + rule + " to path: " + path );
  +        }
  +    }
  +        
   }
  
  
  
  1.17      +33 -20    jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanWriter.java
  
  Index: BeanWriter.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanWriter.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- BeanWriter.java	26 Feb 2002 19:51:32 -0000	1.16
  +++ BeanWriter.java	4 Mar 2002 03:15:54 -0000	1.17
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanWriter.java,v 1.16 2002/02/26 19:51:32 rdonkin Exp $
  - * $Revision: 1.16 $
  - * $Date: 2002/02/26 19:51:32 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/java/org/apache/commons/betwixt/io/BeanWriter.java,v 1.17 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.17 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,7 +57,7 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: BeanWriter.java,v 1.16 2002/02/26 19:51:32 rdonkin Exp $
  + * $Id: BeanWriter.java,v 1.17 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt.io;
   
  @@ -125,7 +125,7 @@
     * 
     * 
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.16 $
  +  * @version $Revision: 1.17 $
     */
   public class BeanWriter {
   
  @@ -230,8 +230,7 @@
                           IntrospectionException {
                       
           
  -        if ( log.isTraceEnabled() )
  -        {
  +        if ( log.isTraceEnabled() ) {
               log.trace( "Writing bean graph (qualified name '" + qualifiedName + "'" );
           }
           
  @@ -250,11 +249,11 @@
                   
                   // only give id's to non-primatives
                   if ( elementDescriptor.isPrimitiveType() ) {
  -                        // write without an id
  -                        write( 
  -                            qualifiedName, 
  -                            elementDescriptor, 
  -                            context );
  +                    // write without an id
  +                    write( 
  +                        qualifiedName, 
  +                        elementDescriptor, 
  +                        context );
                   } 
                   else {
                   
  @@ -450,7 +449,6 @@
   
       /** Express body text */
       protected void expressBodyText(String text) throws IOException {
  -        writer.write( ">" );
           if ( text == null ) {
               // XXX This is probably a programming error
               log.error( "[expressBodyText]Body text is null" );
  @@ -568,11 +566,10 @@
                               throws 
                                   IOException, 
                                   IntrospectionException {        
  -        boolean answer = false;
           ElementDescriptor[] childDescriptors = elementDescriptor.getElementDescriptors();
  +        boolean writtenContent = false;
           if ( childDescriptors != null && childDescriptors.length > 0 ) {
               // process child elements
  -            writer.write( ">" );
               ++indentLevel;
               for ( int i = 0, size = childDescriptors.length; i < size; i++ ) {
                   ElementDescriptor childDescriptor = childDescriptors[i];
  @@ -585,22 +582,35 @@
                           // XXXX: should we handle nulls better
                           if ( childBean instanceof Iterator ) {
                               for ( Iterator iter = (Iterator) childBean; iter.hasNext(); ) {
  +                                if ( ! writtenContent ) {
  +                                    writtenContent = true;
  +                                    writer.write( ">" );
  +                                }
                                   write( qualifiedName, iter.next() );
                               }
                           }
                           else {
  +                            if ( ! writtenContent ) {
  +                                writtenContent = true;
  +                                writer.write( ">" );
  +                            }
                               write( qualifiedName, childBean );
                           }
                       }                    
                   }
                   else {
  +                    if ( ! writtenContent ) {
  +                        writtenContent = true;
  +                        writer.write( ">" );
  +                    }
                       write( childDescriptor.getQualifiedName(), childDescriptor, childContext );
                   }
               }
               --indentLevel;
  -            writePrintln();
  -            writeIndent();
  -            answer = true;
  +            if ( writtenContent ) {
  +                writePrintln();
  +                writeIndent();
  +            }
           }
           else {
               // evaluate the body text 
  @@ -610,13 +620,16 @@
                   if ( value != null ) {
                       String text = escapeBodyValue(value);
                       if ( text != null && text.length() > 0 ) {
  +                        if ( ! writtenContent ) {
  +                            writtenContent = true;
  +                            writer.write( ">" );
  +                        }
                           expressBodyText(text);
  -                        answer = true;
                       }
                   }                
               }
           }
  -        return answer;
  +        return writtenContent;
       }
       
       /** Writes the attribute declarations */
  
  
  
  1.2       +2 -2      jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/RSSBeanReader.java
  
  Index: RSSBeanReader.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/RSSBeanReader.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- RSSBeanReader.java	31 Jan 2002 21:24:42 -0000	1.1
  +++ RSSBeanReader.java	4 Mar 2002 03:15:54 -0000	1.2
  @@ -5,7 +5,7 @@
    * version 1.1, a copy of which has been included with this distribution in
    * the LICENSE file.
    * 
  - * $Id: RSSBeanReader.java,v 1.1 2002/01/31 21:24:42 jstrachan Exp $
  + * $Id: RSSBeanReader.java,v 1.2 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt;
   
  @@ -27,7 +27,7 @@
     * outputs it again.
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.1 $
  +  * @version $Revision: 1.2 $
     */
   public class RSSBeanReader extends AbstractTestCase {
       
  @@ -54,7 +54,7 @@
       public void run(String[] args) throws Exception {
           BeanReader reader = new BeanReader();
           
  -        reader.registerBeanClass( "rss/channel", Channel.class );
  +        reader.registerBeanClass( Channel.class );
           
           // Register local copies of the DTDs we understand
           for (int i = 0; i < registrations.length; i += 2) {
  
  
  
  1.3       +67 -5     jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/TestRSSRoundTrip.java
  
  Index: TestRSSRoundTrip.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/TestRSSRoundTrip.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- TestRSSRoundTrip.java	19 Feb 2002 15:31:30 -0000	1.2
  +++ TestRSSRoundTrip.java	4 Mar 2002 03:15:54 -0000	1.3
  @@ -1,7 +1,7 @@
   /*
  - * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/TestRSSRoundTrip.java,v 1.2 2002/02/19 15:31:30 jstrachan Exp $
  - * $Revision: 1.2 $
  - * $Date: 2002/02/19 15:31:30 $
  + * $Header: /home/cvs/jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/TestRSSRoundTrip.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
  + * $Revision: 1.3 $
  + * $Date: 2002/03/04 03:15:54 $
    *
    * ====================================================================
    *
  @@ -57,7 +57,7 @@
    * information on the Apache Software Foundation, please see
    * <http://www.apache.org/>.
    * 
  - * $Id: TestRSSRoundTrip.java,v 1.2 2002/02/19 15:31:30 jstrachan Exp $
  + * $Id: TestRSSRoundTrip.java,v 1.3 2002/03/04 03:15:54 jstrachan Exp $
    */
   package org.apache.commons.betwixt;
   
  @@ -65,14 +65,17 @@
   import java.io.StringReader;
   import java.io.StringWriter;
   import java.io.Writer;
  +import java.net.URL;
   
   import junit.framework.Test;
   import junit.framework.TestCase;
   import junit.framework.TestSuite;
   import junit.textui.TestRunner;
   
  +import org.apache.commons.betwixt.io.BeanReader;
   import org.apache.commons.betwixt.io.BeanWriter;
   
  +import org.apache.commons.digester.rss.Channel;
   import org.apache.commons.digester.rss.RSSDigester;
   
   import org.apache.commons.logging.Log;
  @@ -85,10 +88,21 @@
     * to check that the document is parseable again.
     *
     * @author <a href="mailto:jstrachan@apache.org">James Strachan</a>
  -  * @version $Revision: 1.2 $
  +  * @version $Revision: 1.3 $
     */
   public class TestRSSRoundTrip extends AbstractTestCase {
       
  +    /**
  +     * The set of public identifiers, and corresponding resource names,
  +     * for the versions of the DTDs that we know about.
  +     */
  +    protected static final String registrations[] = {
  +        "-//Netscape Communications//DTD RSS 0.9//EN",
  +        "/org/apache/commons/digester/rss/rss-0.9.dtd",
  +        "-//Netscape Communications//DTD RSS 0.91//EN",
  +        "/org/apache/commons/digester/rss/rss-0.91.dtd",
  +    };
  +    
       public static void main( String[] args ) {
           TestRunner.run( suite() );
       }
  @@ -118,6 +132,54 @@
           // now lets try parse again
           String text = buffer.toString();        
           bean = digester.parse( new StringReader( text ) );
  +        
  +        // managed to parse it again!
  +        
  +        // now lets write it to another buffer
  +        buffer = new StringWriter();
  +        write( bean, buffer );
  +        
  +        String text2 = buffer.toString();
  +
  +        // if the two strings are equal then we've done a full round trip
  +        // with the XML staying the same. Though the original source XML
  +        // could well be different
  +        assertEquals( "Round trip value should remain unchanged", text, text2 );
  +    }
  +    
  +    /** 
  +     * This tests using the both the RSSDigester 
  +     * and the BeanReader to parse an RSS and output it
  +     * using the BeanWriter
  +     */
  +    public void testBeanWriterRoundTrip() throws Exception {
  +        // lets parse the example using the RSSDigester
  +        RSSDigester digester = new RSSDigester();
  +        
  +        InputStream in = getClass().getResourceAsStream( "rss-example.xml" );
  +        Object bean = digester.parse( in ); 
  +        in.close();
  +        
  +        // now lets output it to a buffer
  +        StringWriter buffer = new StringWriter();
  +        write( bean, buffer );
  +        
  +
  +        // create a BeanReader
  +        BeanReader reader = new BeanReader();
  +        reader.registerBeanClass( Channel.class );
  +
  +        // Register local copies of the DTDs we understand
  +        for (int i = 0; i < registrations.length; i += 2) {
  +            URL url = RSSDigester.class.getResource(registrations[i + 1]);
  +            if (url != null) {
  +                reader.register(registrations[i], url.toString());
  +            }
  +        }
  +        
  +        // now lets try parse the output sing the BeanReader 
  +        String text = buffer.toString();        
  +        bean = reader.parse( new StringReader( text ) );
           
           // managed to parse it again!
           
  
  
  
  1.2       +68 -65    jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/rss-example.xml
  
  Index: rss-example.xml
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/betwixt/rss-example.xml,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- rss-example.xml	31 Jan 2002 19:56:03 -0000	1.1
  +++ rss-example.xml	4 Mar 2002 03:15:54 -0000	1.2
  @@ -1,65 +1,68 @@
  -<?xml version="1.0"?>
  -
  -<!DOCTYPE rss PUBLIC
  - "-//Netscape Communications//DTD RSS 0.91//EN"
  - "http://my.netscape.com/publish/formats/rss-0.91.dtd">
  -
  -<rss version="0.91">
  -
  -  <channel>
  -
  -    <title>MozillaZine</title>
  -    <link>http://www.mozillazine.org</link>
  -    <description>Your source for Mozilla news, advocacy,
  -      interviews, builds, and more!</description>
  -    <language>en-US</language>
  -    <rating>(PICS-1.1 "http://www.rsac.org/ratingsv01.html"
  -      2 gen true comment "RSACi North America Server"
  -      for "http://www.rsac.org" on "1996.04.16T08:15-0500"
  -      r (n 0 s 0 v 0 l 0))</rating>
  -
  -    <image>
  -      <title>MozillaZine</title>
  -      <url>http://www.mozillazine.org/image/mynetscape88.gif</url>
  -      <link>http://www.mozillazine.org</link>
  -      <width>88</width>
  -      <height>31</height>
  -      <description>Articles, discussions, builds, and more...</description>
  -    </image>
  -
  -    <item>
  -      <title>Java2 in Navigator 5?</title>
  -      <link>http://www.mozillazine.org/talkback.html?article=607</link>
  -      <description>Will Java2 be an integrated part of Navigator 5?
  -      Read more about it in this discussion...</description>
  -    </item>
  -
  -    <item>
  -      <title>Communicator 4.61 Out</title>
  -      <link>http://www.mozillazine.org/talkback.html?article=606</link>
  -      <description>The latest version of Communicator is now
  -      available.  It includes security enhancements
  -      and various bug fixes.</description>
  -    </item>
  -
  -    <item>
  -      <title>Mozilla Dispenses with Old,
  -      Proprietary DOM</title>
  -      <link>http://www.mozillazine.org/talkback.html?article=604</link>
  -    </item>
  -
  -    <item>
  -      <title>The Animation Contest is Now Closed</title>
  -      <link>http://www.mozillazine.org/talkback.html?article=603</link>
  -    </item>
  -
  -    <textinput>
  -      <title>Send</title>
  -      <description>Comments about MozillaZine?</description>
  -      <name>responseText</name>
  -      <link>http://www.mozillazine.org/cgi-bin/sampleonly.cgi</link>
  -    </textinput>
  -
  -  </channel>
  -
  -</rss>
  +<?xml version="1.0"?>
  +
  +<!DOCTYPE rss PUBLIC
  + "-//Netscape Communications//DTD RSS 0.91//EN"
  + "http://my.netscape.com/publish/formats/rss-0.91.dtd">
  +
  +<rss version="0.91">
  +
  +  <channel>
  +
  +    <title>MozillaZine</title>
  +    <link>http://www.mozillazine.org</link>
  +    <description>Your source for Mozilla news, advocacy,
  +      interviews, builds, and more!</description>
  +    <language>en-US</language>
  +    <rating>(PICS-1.1 "http://www.rsac.org/ratingsv01.html"
  +      2 gen true comment "RSACi North America Server"
  +      for "http://www.rsac.org" on "1996.04.16T08:15-0500"
  +      r (n 0 s 0 v 0 l 0))</rating>
  +
  +    <image>
  +      <title>MozillaZine</title>
  +      <url>http://www.mozillazine.org/image/mynetscape88.gif</url>
  +      <link>http://www.mozillazine.org</link>
  +      <width>88</width>
  +      <height>31</height>
  +      <description>Articles, discussions, builds, and more...</description>
  +    </image>
  +
  +    <item>
  +      <title>Java2 in Navigator 5?</title>
  +      <link>http://www.mozillazine.org/talkback.html?article=607</link>
  +      <description>Will Java2 be an integrated part of Navigator 5?
  +      Read more about it in this discussion...</description>
  +    </item>
  +
  +    <item>
  +      <title>Communicator 4.61 Out</title>
  +      <link>http://www.mozillazine.org/talkback.html?article=606</link>
  +      <description>The latest version of Communicator is now
  +      available.  It includes security enhancements
  +      and various bug fixes.</description>
  +    </item>
  +
  +    <item>
  +      <title>Mozilla Dispenses with Old,
  +      Proprietary DOM</title>
  +      <link>http://www.mozillazine.org/talkback.html?article=604</link>
  +    </item>
  +
  +    <item>
  +      <title>The Animation Contest is Now Closed</title>
  +      <link>http://www.mozillazine.org/talkback.html?article=603</link>
  +    </item>
  +
  +    <textinput>
  +      <title>Send</title>
  +      <description>Comments about MozillaZine?</description>
  +      <name>responseText</name>
  +      <link>http://www.mozillazine.org/cgi-bin/sampleonly.cgi</link>
  +    </textinput>
  +
  +    <skipDays>
  +      <day>Tuesday</day>
  +    </skipDays>
  +  </channel>
  +
  +</rss>
  
  
  
  1.3       +6 -0      jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/digester/rss/Channel.betwixt
  
  Index: Channel.betwixt
  ===================================================================
  RCS file: /home/cvs/jakarta-commons-sandbox/betwixt/src/test/org/apache/commons/digester/rss/Channel.betwixt,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Channel.betwixt	19 Feb 2002 15:31:30 -0000	1.2
  +++ Channel.betwixt	4 Mar 2002 03:15:54 -0000	1.3
  @@ -6,6 +6,12 @@
         <element name="title"  property="title"/>
         <element name="item"  property="items"/>
         <element name="textinput"  property="textInput"/>
  +      <element name="skipDays">
  +        <element name="day" property="skipDays"/>
  +      </element>
  +      <element name="skipHours">
  +        <element name="hour" property="skipHours"/>
  +      </element>
         <addDefaults/>
       </element>
     </element>
  
  
  

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>