robert burrell donkin wrote:
hi Michael

i'm pretty sure that it's a lack of capability in the code. i have been meaning to revise this area but i've been held by by some classloader problems in maven that meant i couldn't write the test cases i needed.

these problems now seems to be resolved so i'll try to find time to take another look.
Thanks. If there is consensus with my analysis and consensus that the second solution should be implemented (see my follow up email), here are some patches that fix the problem.

Changes:
o Changed ElementDescriptor.hasAttributes() to call ElementDescriptor.getAttributeDescriptors() if attributes == null
o Added innerBean helper to AbstractTestClass
o Added innerBean tests to TextXMLIntrospector, refactored
o Added Nodes class and matching betwixt configuration
o Minor documentation changes (better definition of method contracts) in a couple of places

Paths to new files:
src/test/org/apache/commons/betwixt/xmlunit/Nodes.java
src/test/org/apache/commons/betwixt/xmlunit/Nodes$Node.betwixt

--
Michael
Index: jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/AbstractTestCase.java =================================================================== RCS file: /home/cvspublic/jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/AbstractTestCase.java,v retrieving revision 1.2 diff -u -r1.2 AbstractTestCase.java --- jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/AbstractTestCase.java 26 Jul 2002 21:04:05 -0000 1.2 +++ jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/AbstractTestCase.java 27 Oct 2002 23:54:42 -0000 @@ -70,11 +70,13 @@ import junit.framework.TestSuite; import org.apache.commons.betwixt.io.BeanWriter; - +import org.apache.commons.betwixt.xmlunit.Nodes; +import org.apache.commons.betwixt.xmlunit.Nodes.Node; /** Abstract base class for test cases. * * @author James Strachan + * @author Michael Davey * @version $Revision: 1.6 $ */ public abstract class AbstractTestCase extends TestCase { @@ -117,6 +119,13 @@ address.setCode( "N5" ); bean.setAddress( address ); + return bean; + } + + protected Object createInnerBean() { + Node bean = new Nodes.Node(); + bean.setId( "foo" ); + bean.setType( "Manager" ); return bean; } }

==== End ====
Index: jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/ElementDescriptor.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/ElementDescriptor.java,v
retrieving revision 1.3
diff -u -r1.3 ElementDescriptor.java
--- jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/ElementDescriptor.java	1 Jul 2002 18:43:00 -0000	1.3
+++ jakarta-commons/betwixt/src/java/org/apache/commons/betwixt/ElementDescriptor.java	27 Oct 2002 23:53:19 -0000
@@ -74,6 +74,7 @@
   *
   * @author James Strachan
   * @author Martin van den Bemt
+  * @author Michael Davey
   * @version $Revision: 1.3 $
   */
 public class ElementDescriptor extends NodeDescriptor {
@@ -143,8 +144,15 @@
         return elementDescriptors != null && elementDescriptors.length > 0;
     }
     
-    /** Returns true if this element has attributes */
+    /**
+     *  Returns true if this element has attributes.
+     *  Gets the attribute descriptors if they havn't been read yet.
+     *  @return true if this element has attributes, false otherwise
+     */
     public boolean hasAttributes() {
+        if ( attributeDescriptors == null ) {
+            getAttributeDescriptors();
+        }
         return attributeDescriptors != null && attributeDescriptors.length > 0;
     }
     
@@ -172,7 +180,12 @@
     }
     
     
-    /** Returns the attribute descriptors for this element */
+    /**
+     *  Returns the attribute descriptors for this element.
+     *  This method never returns null
+     *  @return the attribute descriptors for this element,
+     *  or an empty array if this element has not attributes
+     */
     public AttributeDescriptor[] getAttributeDescriptors() {
         if ( attributeDescriptors == null ) {
             if ( attributeList == null ) {

==== End ====
Index: jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/TestXMLIntrospector.java
===================================================================
RCS file: /home/cvspublic/jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/TestXMLIntrospector.java,v
retrieving revision 1.1
diff -u -r1.1 TestXMLIntrospector.java
--- jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/TestXMLIntrospector.java	10 Jun 2002 17:53:32 -0000	1.1
+++ jakarta-commons/betwixt/src/test/org/apache/commons/betwixt/TestXMLIntrospector.java	27 Oct 2002 23:55:36 -0000
@@ -72,6 +72,7 @@
 /** Test harness for the XMLIntrospector
   *
   * @author James Strachan
+  * @author Michael Davey
   * @version $Revision: 1.8 $
   */
 public class TestXMLIntrospector extends AbstractTestCase {
@@ -95,61 +96,76 @@
         Object bean = createBean();
         
         XMLBeanInfo info = introspector.introspect( bean );
-        
-        assertTrue( "Found XMLBeanInfo", info != null );
-        
-        ElementDescriptor descriptor = info.getElementDescriptor();
-        
-        assertTrue( "Found root element descriptor", descriptor != null );
-        
-        AttributeDescriptor[] attributes = descriptor.getAttributeDescriptors();
-        
-        assertTrue( "Found attributes", attributes != null && attributes.length > 0 );
+        helperTestIntrospector( bean, introspector );
         
         // test second introspection with caching on
         info = introspector.introspect( bean );
-        
-        assertTrue( "Found XMLBeanInfo", info != null );
-        
-        descriptor = info.getElementDescriptor();
-        
-        assertTrue( "Found root element descriptor", descriptor != null );
-        
-        attributes = descriptor.getAttributeDescriptors();
-        
-        assertTrue( "Found attributes", attributes != null && attributes.length > 0 );
-
+        helperTestIntrospector( bean, introspector );
 
         // test introspection with caching off      
         introspector.setCachingEnabled(false);  
         info = introspector.introspect( bean );
+        helperTestIntrospector( bean, introspector );
+
+        // test introspection after flushing cache
+        introspector.setCachingEnabled(true); 
+        introspector.flushCache();
+        helperTestIntrospector( bean, introspector );
+
+    }
+    
+    public void testIntrospectorWithInnerClass() throws Exception {
+        XMLIntrospector introspector = new XMLIntrospector();
+        introspector.setAttributesForPrimitives(true);
+        Object bean = createInnerBean();
         
-        assertTrue( "Found XMLBeanInfo", info != null );
-        
-        descriptor = info.getElementDescriptor();
+        // test introspection with caching on
+        XMLBeanInfo info = helperTestIntrospector( bean, introspector );
+        AttributeDescriptor idAttribute = info.getIDAttribute();
+        assertNotNull( "ID not found(@1)", idAttribute );
         
-        assertTrue( "Found root element descriptor", descriptor != null );
+        // test second introspection with caching on
+        info = helperTestIntrospector( bean, introspector );
+        idAttribute = info.getIDAttribute();
+        assertNotNull( "ID not found(@2)", idAttribute );
         
-        attributes = descriptor.getAttributeDescriptors();
+        // test introspection with caching off      
+        introspector.setCachingEnabled(false);  
+        info = helperTestIntrospector( bean, introspector );
+        idAttribute = info.getIDAttribute();
+        assertNotNull( "ID not found(@3)", idAttribute );
         
-        assertTrue( "Found attributes", attributes != null && attributes.length > 0 );
-
-
         // test introspection after flushing cache
         introspector.setCachingEnabled(true); 
         introspector.flushCache();
-        info = introspector.introspect( bean );
-        
-        assertTrue( "Found XMLBeanInfo", info != null );
+        info = helperTestIntrospector( bean, introspector );
+        idAttribute = info.getIDAttribute();
+        assertNotNull( "ID not found(@4)", idAttribute );
+    }
+    
+    /** convenience method */
+    protected XMLBeanInfo helperTestIntrospector( Object bean, XMLIntrospector introspector )
+    throws Exception
+    {
+        XMLBeanInfo info = introspector.introspect( bean );
         
-        descriptor = info.getElementDescriptor();
+        assertNotNull( "XMLBeanInfo not found", info );
         
-        assertTrue( "Found root element descriptor", descriptor != null );
+        assertEquals( "id not set", "id", info.getIDAttributeName() );
+
+        ElementDescriptor descriptor = info.getElementDescriptor();   
+        assertNotNull( "no root element descriptor", descriptor );
         
-        attributes = descriptor.getAttributeDescriptors();
+        assertTrue( "no attributes(@1)", info.getElementDescriptor().hasAttributes() );
         
-        assertTrue( "Found attributes", attributes != null && attributes.length > 0 );
+        AttributeDescriptor[] attributes = descriptor.getAttributeDescriptors(); 
+        assertTrue( "no attributes(@2)", attributes != null && attributes.length > 0 );
+
+        attributes = info.getElementDescriptor().getAttributeDescriptors(); 
+        assertTrue( "no attributes(@3)", attributes != null && attributes.length > 0 );
 
+        assertTrue( "no attributes(@4)", descriptor.hasAttributes() );
+        return info;
     }
 }
 
 
==== End ====
/*
 *  @(#) Nodes.java  $Revision$  2002-08-18
 *
 * ====================================================================
 * The Apache Software License, Version 1.1
 *
 * Copyright (c) 2000 The Apache Software Foundation.  All rights
 * reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 * 3. The end-user documentation included with the redistribution,
 *    if any, must include the following acknowledgment:
 *       "This product includes software developed by the
 *        Apache Software Foundation (http://www.apache.org/)."
 *    Alternately, this acknowledgment may appear in the software itself,
 *    if and wherever such third-party acknowledgments normally appear.
 *
 * 4. The names "Apache" and "Apache Software Foundation" must
 *    not be used to endorse or promote products derived from this
 *    software without prior written permission. For written
 *    permission, please contact [EMAIL PROTECTED]
 *
 * 5. Products derived from this software may not be called "Apache",
 *    nor may "Apache" appear in their name, without prior written
 *    permission of the Apache Software Foundation.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 * ====================================================================
 *
 * This software consists of voluntary contributions made by many
 * individuals on behalf of the Apache Software Foundation.  For more
 * information on the Apache Software Foundation, please see
 * <http://www.apache.org/>.
 *
 * Portions of this software are based upon open source software
 * originally written for CJAN <http://cjan.org/>
 *
 *  @date Created on August 18, 2002, 12:05 AM
 *  $Id$
 */

package org.apache.commons.betwixt.xmlunit;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.commons.betwixt.*;

/**
 *  A singleton class representing the all the nodes
 *  about which a repository knows
 *
 *  @author  Michael Davey <Michael.Davey at CodeRage.ORG>
 *  @version @version@ (file revision $Revision$)
 */
public class Nodes {

    private static HashMap nodes = new HashMap();
    private static Nodes singleton = new Nodes();

    // for inner class
    // type should be UpperCamelCase in xml, lower case here for ease of comparison
    private static final Vector allowedTypes = new Vector(
        (Collection)Arrays.asList( new String[] { "root", "manager", "archive" } ) );
    
    /**
     *  Creates a new Nodes object
     *  @deprecated - internal use only
     */
    public Nodes() {
    }
    
    /** get the singleton */
    public static Nodes getInstance() {
        return singleton;
    }
    
    /**
     *  Get this class's name
     *  @exception returns null
     *  @deprecated anyone using this?
     */
    public static Class getName() {
        return ( Nodes.class );
    }
    
    /**
     *  Creating a new node
     *  @throws RuntimeException if unique key is already in use
     */
    public Nodes addNode( String name, String type, String[] digests ) {
        Node tmp = new Node( name, type, digests );
        return addNode( tmp );
    }
    
    /**
     *  Creating a new node
     */
    public Nodes addNode( Node node ) {
        nodes.put( node.getId(), node );
        return getInstance();
    }
    
    /**
     *  Updating an existing node
     */
    public Nodes updateNode( Node node ) {
        nodes.put( node.getId(), node );
        return getInstance();
    }
    
    /**
     *  Is <code>name</code> a node name in this Collection?
     *  @return true if node name is known (in use), false otherwise
     */
    public static boolean isId( String name ) {
        return nodes.containsKey( name );
    }
    
    /**
     *  Returns the number of Node beans in this Nodes collection
     */
    public int size() {
        return nodes.size();
    }
    
    /**
     *  @return a Collection of all the nodes
     *
     *  The returned collection must be treated as immutable
     */
    public final synchronized List getNodesAsList() {
        List l = new ArrayList( nodes.values() );
        if ( l == null ) throw new NullPointerException( "l == null" );
        return l;
    }
    
    /**
     *  @return a Collection of all the nodes
     *
     *  The returned collection must be treated as immutable
     */
    public final Collection getNodes() {
        List l = new ArrayList( nodes.values() );
        if ( l == null ) throw new NullPointerException( "l == null" );
        return l;
    }
    
    /**
     *  Update the internal node list with the supplied list
     */
    public void updateNodes( Collection nodes ) {
        Iterator i = nodes.iterator();
        while( i.hasNext() ) {
            Node l = (Node) i.next();
            String name = l.getId();
            if ( isId( name ) ) {
                updateNode( l );
            } else {
                addNode( l );
            }
        }
    }
    
    void writeObject( ObjectOutputStream out )
    throws IOException
    {
        out.writeObject( this );
    }
    
    void readObject( ObjectInputStream in )
    throws IOException
    {
    }
    
    void writeSchema( java.io.ObjectOutput out )
    throws IOException
    {
    }
    
    public String toString() {
        return "[" + this.getClass().getName() + ": " + nodes.toString() + "]";
    }
    
    public static void testHook(String[] args) {
        singleton.addNode( "cjan", "Manager", new String[] {} );
    }
    
    /**
     *  An inner class that represents a single node
     *  @author  Michael Davey <Michael.Davey at CodeRage.ORG>
     */
    public static class Node
    {     
        private String[] digests;
        private String type;
        private String name;
        
        /** @deprecated - only for use during deserialization */
        public Node() {
        }
        
        /**
         *  Creates new Node
         *  @throws RuntimeException if unique key is already in use
         */
        public Node(String name, String type, String[] digests) {
            if ( name == null || type == null || digests == null ) {
                throw new NullPointerException( "name, type or digests == null" );
            }
            if ( isId( name ) ) {
                throw new RuntimeException( "Node: Name '" + name
                + "' already exists" );
            }
            this.setId( name );
            this.setType( type );
            this.digests = digests;
        }
        
        /**
         *  Get the node type ("Root", "Manager" or "Archive")
         */
        public String getType() {
            return type;
        }
        
        /**
         *  Set the node type
         *  @prarm type must be one of "Root", "Manager" or "Archive".
         */
        public void setType( String type ) {
            if ( type == null ) throw new NullPointerException( "type == null" );
            if ( type.equals("") ) {
                throw new IllegalArgumentException( "Illegal type: \"\"" );
            }
            if ( ! allowedTypes.contains( type.toLowerCase() ) ) {
                throw new IllegalArgumentException( "Type must be one of: "
                + allowedTypes.toString() );
            }
            this.type = type;
        }
        
        public String getId() {
            return name;
        }
        
        public void setId( String name ) {
            if ( name == null ) throw new NullPointerException( "name == null" );
            if ( name.equals("") ) {
                throw new IllegalArgumentException( "Illegal name: \"\"" );
            }
            this.name = name;
        }
        
        void writeObject( ObjectOutputStream out )
        throws IOException {
            out.writeObject( (Object)this );
        }
        
        void readObject( java.io.ObjectInput in )
        throws IOException {
        }
        
        void writeSchema( java.io.ObjectOutput out )
        throws IOException {
        }
        
        public String toString() {
            return "[" + this.getClass().getName() + ": name=" + name + " type="
                    + type + " digests=" + digests + "]";
        }
        
    } // end inner class
}
<?xml version="1.0" encoding="UTF-8" ?>
<info primitiveTypes="attribute">
  <element name="node">
    <attribute name="file" value="$"/>
    <attribute name="type" />
    <addDefaults />
  </element>
</info>

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to