You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rd...@apache.org on 2005/04/17 10:43:25 UTC

svn commit: r161631 - in jakarta/commons/proper/betwixt/trunk: src/java/org/apache/commons/betwixt/ src/java/org/apache/commons/betwixt/digester/ src/java/org/apache/commons/betwixt/io/read/ src/java/org/apache/commons/betwixt/registry/ xdocs/ xdocs/guide/

Author: rdonkin
Date: Sun Apr 17 01:43:23 2005
New Revision: 161631

URL: http://svn.apache.org/viewcvs?view=rev&rev=161631
Log:
Added support for polymorphic elements. Contributed by Thomas Dudziak. Bugzilla issue #34443.

Modified:
    jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/ElementDescriptor.java
    jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/XMLIntrospector.java
    jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java
    jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ChainedBeanCreatorFactory.java
    jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ReadContext.java
    jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/registry/DefaultXMLBeanInfoRegistry.java
    jakarta/commons/proper/betwixt/trunk/xdocs/guide/reading.xml
    jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml

Modified: jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/ElementDescriptor.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/ElementDescriptor.java?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/ElementDescriptor.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/ElementDescriptor.java Sun Apr 17 01:43:23 2005
@@ -665,4 +665,15 @@
             this.useBindTimeTypeForMapping = new Boolean(useBindTimeTypeForMapping);
         }
     }
+
+    /**
+     * Is this a polymorphic element?
+     * A polymorphic element's name is not fixed at 
+     * introspection time and it's resolution is postponed to bind time.
+     * @return true if {@link #getQualifiedName} is null, 
+     * false otherwise
+     */
+    public boolean isPolymorphic() { 
+        return (getQualifiedName() == null);
+    }
 }

Modified: jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/XMLIntrospector.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/XMLIntrospector.java?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/XMLIntrospector.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/XMLIntrospector.java Sun Apr 17 01:43:23 2005
@@ -43,6 +43,7 @@
 import org.apache.commons.betwixt.expression.MethodUpdater;
 import org.apache.commons.betwixt.expression.StringExpression;
 import org.apache.commons.betwixt.registry.DefaultXMLBeanInfoRegistry;
+import org.apache.commons.betwixt.registry.PolymorphicReferenceResolver;
 import org.apache.commons.betwixt.registry.XMLBeanInfoRegistry;
 import org.apache.commons.betwixt.strategy.ClassNormalizer;
 import org.apache.commons.betwixt.strategy.DefaultNameMapper;
@@ -82,7 +83,7 @@
     protected Log log = LogFactory.getLog( XMLIntrospector.class );
     
     /** Maps classes to <code>XMLBeanInfo</code>'s */
-    private XMLBeanInfoRegistry registry = new DefaultXMLBeanInfoRegistry();
+    private XMLBeanInfoRegistry registry;
     
     /** Digester used to parse the XML descriptor files */
     private XMLBeanInfoDigester digester;
@@ -93,6 +94,14 @@
     /** Configuration to be used for introspection*/
     private IntrospectionConfiguration configuration;
     
+    /**
+     * Resolves polymorphic references.
+     * Though this is used only at bind time,
+     * it is typically tightly couple to the xml registry. 
+     * It is therefore convenient to keep both references together.
+     */
+    private PolymorphicReferenceResolver polymorphicReferenceResolver;
+    
     /** Base constructor */
     public XMLIntrospector() {
         this(new IntrospectionConfiguration());
@@ -106,6 +115,10 @@
      */
     public XMLIntrospector(IntrospectionConfiguration configuration) {
         setConfiguration(configuration);
+        DefaultXMLBeanInfoRegistry defaultRegistry 
+            = new DefaultXMLBeanInfoRegistry();
+        setRegistry(defaultRegistry);
+        setPolymorphicReferenceResolver(defaultRegistry);
     }
     
     
@@ -154,6 +167,11 @@
      * It also allows the standard introspection mechanism 
      * to be overridden on a per class basis.</p>
      *
+     * <p><strong>Note</strong> when using polymophic mapping with a custom
+     * registry, a call to 
+     * {@link #setPolymorphicReferenceResolver(PolymorphicReferenceResolver)}
+     * may be necessary.
+     * </p>
      * @param registry the XMLBeanInfoRegistry to use
      */
     public void setRegistry(XMLBeanInfoRegistry registry) {
@@ -212,6 +230,47 @@
       */    
     public void setClassNormalizer(ClassNormalizer classNormalizer) {
         getConfiguration().setClassNormalizer(classNormalizer);
+    }
+    
+    
+    
+    /**
+     * <p>Gets the resolver for polymorphic references.</p>
+     * <p>
+     * Though this is used only at bind time,
+     * it is typically tightly couple to the xml registry. 
+     * It is therefore convenient to keep both references together.
+     * </p>
+     * <p><strong>Note:</strong> though the implementation is
+     * set initially to the default registry,
+     * this reference is not updated when {@link #setRegistry(XMLBeanInfoRegistry)}
+     * is called. Therefore, a call to {@link #setPolymorphicReferenceResolver(PolymorphicReferenceResolver)}
+     * with the instance may be necessary. 
+     * </p>
+     * @return <code>PolymorphicReferenceResolver</code>, not null
+     */
+    public PolymorphicReferenceResolver getPolymorphicReferenceResolver() {
+        return polymorphicReferenceResolver;
+    }
+    
+    /**
+     * <p>Sets the resolver for polymorphic references.</p>
+     * <p>
+     * Though this is used only at bind time,
+     * it is typically tightly couple to the xml registry. 
+     * It is therefore convenient to keep both references together.
+     * </p>
+     * <p><strong>Note:</strong> though the implementation is
+     * set initially to the default registry,
+     * this reference is not updated when {@link #setRegistry(XMLBeanInfoRegistry)}
+     * is called. Therefore, a call to {@link #setPolymorphicReferenceResolver(PolymorphicReferenceResolver)}
+     * with the instance may be necessary. 
+     * </p>
+     * @param polymorphicReferenceResolver The polymorphicReferenceResolver to set.
+     */
+    public void setPolymorphicReferenceResolver(
+            PolymorphicReferenceResolver polymorphicReferenceResolver) {
+        this.polymorphicReferenceResolver = polymorphicReferenceResolver;
     }
     
     /** 

Modified: jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/digester/ElementRule.java Sun Apr 17 01:43:23 2005
@@ -112,7 +112,8 @@
         
         // check that the name attribute is present 
         if ( !isCollective && (nameAttributeValue == null || nameAttributeValue.trim().equals( "" ) )) {
-            throw new SAXException("Name attribute is required.");
+            // allow polymorphic mappings but log note for user
+            log.info("No name attribute has been specified. This element will be polymorphic.");
         }
         
         // check that name is well formed 

Modified: jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ChainedBeanCreatorFactory.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ChainedBeanCreatorFactory.java?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ChainedBeanCreatorFactory.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ChainedBeanCreatorFactory.java Sun Apr 17 01:43:23 2005
@@ -16,6 +16,7 @@
 package org.apache.commons.betwixt.io.read;
 
 import org.apache.commons.betwixt.ElementDescriptor;
+import org.apache.commons.betwixt.registry.PolymorphicReferenceResolver;
 import org.apache.commons.logging.Log;
 
 /**  
@@ -85,8 +86,17 @@
                 
                 ElementDescriptor descriptor = element.getDescriptor();
                 if ( descriptor != null ) {
-                    // created based on implementation class
-                    theClass = descriptor.getImplementationClass();
+                    // check for polymorphism 
+                    if (descriptor.isPolymorphic()) {
+                        theClass = context.getXMLIntrospector().getPolymorphicReferenceResolver()
+                            .resolveType(element, context);
+                    }
+                    
+                    if (theClass == null)
+                    {
+                        // created based on implementation class
+                        theClass = descriptor.getImplementationClass();
+                    }
                 }
                 
                 if ( theClass == null ) {

Modified: jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ReadContext.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ReadContext.java?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ReadContext.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/io/read/ReadContext.java Sun Apr 17 01:43:23 2005
@@ -526,4 +526,6 @@
         return result;  
     }
 
+
+
 }

Modified: jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/registry/DefaultXMLBeanInfoRegistry.java
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/registry/DefaultXMLBeanInfoRegistry.java?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/registry/DefaultXMLBeanInfoRegistry.java (original)
+++ jakarta/commons/proper/betwixt/trunk/src/java/org/apache/commons/betwixt/registry/DefaultXMLBeanInfoRegistry.java Sun Apr 17 01:43:23 2005
@@ -16,10 +16,15 @@
  * limitations under the License.
  */ 
 
+import java.util.Collection;
 import java.util.HashMap;
+import java.util.Iterator;
 import java.util.Map;
 
+import org.apache.commons.betwixt.ElementDescriptor;
 import org.apache.commons.betwixt.XMLBeanInfo;
+import org.apache.commons.betwixt.io.read.ElementMapping;
+import org.apache.commons.betwixt.io.read.ReadContext;
 
 /** The default caching implementation.
   * A hashmap is used.
@@ -27,7 +32,7 @@
   * @author <a href="mailto:rdonkin@apache.org">Robert Burrell Donkin</a>
   * @version $Id$
   */
-public class DefaultXMLBeanInfoRegistry implements XMLBeanInfoRegistry {
+public class DefaultXMLBeanInfoRegistry implements XMLBeanInfoRegistry, PolymorphicReferenceResolver {
 
     /** Used to associated <code>XMLBeanInfo</code>'s to classes */
     private Map xmlBeanInfos = new HashMap();
@@ -58,5 +63,37 @@
       */
     public void flush() {
         xmlBeanInfos.clear();
+    }
+
+    /**
+     * Checks all registered <code>XMLBeanInfo</code>'s for the
+     * first suitable match. 
+     * If a suitable one is found, then the class of that info is used. 
+     * @see org.apache.commons.betwixt.registry.PolymorphicReferenceResolver#resolveType(org.apache.commons.betwixt.io.read.ElementMapping, org.apache.commons.betwixt.io.read.ReadContext)
+     */
+    public Class resolveType(ElementMapping mapping, ReadContext context) {
+        Class result = null;
+        Collection cachedClasses = getCachedClasses();
+        ElementDescriptor mappedDescriptor = mapping.getDescriptor();
+        for (Iterator it = cachedClasses.iterator(); it.hasNext();) {
+            XMLBeanInfo  beanInfo  = get((Class)it.next());
+            ElementDescriptor typeDescriptor = beanInfo.getElementDescriptor();
+            if (mapping.getName().equals(typeDescriptor.getQualifiedName()) &&
+                    mappedDescriptor.getPropertyType().isAssignableFrom(beanInfo.getBeanClass())) {
+                result = beanInfo.getBeanClass();
+                break;
+            }
+        }
+        return result;
+    }
+    
+    /**
+     * Gets all classes that are cached in this registry.
+     * 
+     * @return The classes
+     */
+    private Collection getCachedClasses()
+    {
+        return xmlBeanInfos.keySet();
     }
 }

Modified: jakarta/commons/proper/betwixt/trunk/xdocs/guide/reading.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/xdocs/guide/reading.xml?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/xdocs/guide/reading.xml (original)
+++ jakarta/commons/proper/betwixt/trunk/xdocs/guide/reading.xml Sun Apr 17 01:43:23 2005
@@ -323,6 +323,61 @@
 available.
         </p>
     </subsection>
+    	<subsection name='Reading Polymorphic Mappings'>
+    		<p>
+A polymophic mapping is one where the decision about the element type and name
+is postponed from introspection time to bind time. This allows reading of
+collections containing mixed types distinguished by element name. For example:
+		</p>    		
+<source><![CDATA[
+<?xml version="1.0" ?>
+<container>
+  <elementA/>
+  <elementB/>
+</container>
+]]></source>
+			<p>
+Polymorphic mappings should be set up using dot betwixt files. 
+The name attribute for the element should be omitted
+and mappings registered for the contained object types.
+It is usually more convenient to use a single file containing all the mappings.
+For example:
+		</p>
+<source><![CDATA[
+<?xml version="1.0"?>
+<betwixt-config>
+  <class name='SomeContainer'>
+    <element name='container'>
+      <!-- Polymorphic so no name attribute -->
+      <element property='element'/>
+    </element>
+  </class>
+  <!-- Need to register mappings for types of contained elements 
+       (when Betwixt default strategy is used) -->
+  <class name='ContainedElementA'>
+    <element name='elementA'/>
+  </class>
+  <class name='ContainedElementB'>
+    <element name='elementB'/>
+  </class>
+</betwixt-config>
+]]></source>
+			<p>
+By default, in this circumstance Betwixt will try to guess the correct resolution
+by searching all registered <code>XMLBeanInfo</code>'s for an appropriate match.
+If more thn one is found, an arbitrary one is used. 
+In many cases, this accords well with intuition. There are
+occasions when more finely grained control may be required. The resolution is
+therefore factored into <code>PolymorphicReferenceResolver</code> 
+(a pluggable strategy) on <code>XMLIntrospector</code>. 
+A custom resolver allows alternative algorithms for determining
+type to be used which can (for example) ignore the mappings registered.
+Note that the default implementation is provided by the default
+<code>XMLBeanInfoRegistry</code> implementation.
+Therefore, when using a custom registry a custom resolver must also
+be used.
+		</p>
+    </subsection>
 </section>
 
 </body>

Modified: jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml
URL: http://svn.apache.org/viewcvs/jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml?view=diff&r1=161630&r2=161631
==============================================================================
--- jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml (original)
+++ jakarta/commons/proper/betwixt/trunk/xdocs/tasks.xml Sun Apr 17 01:43:23 2005
@@ -186,6 +186,10 @@
 <section name='Completed'>
     <subsection name='since 0.6'>
         <ul>
+            <li>Added support for <strong>polymorphic mappings</strong>. 
+            This allows the type of a property to be guessed at bind time
+            (rather than at compile time).
+            </li>
         	<li>Added <strong>options to context</strong>. This replaces direct flavour mechanism. 
         	(Flavour becomes just a specific option).</li>
         	<li>Factored out <strong>id storage</strong> into strategy</li>



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org