Hi Ajith,

I've attached a patch against Axis2 0.94 that appears to address the problem.  All the unit tests pass and the code for recursive types appears to be correct, but there could be other adverse consequences I haven't hit yet.

Hope this is useful in addressing the issue,

Chuck


"Ajith Ranabahu" <[EMAIL PROTECTED]> wrote on 03/02/2006 05:58:08 PM:

Hi Chuck,
Hmm.. Valid scenario and humbly the recursive code that processes the
schema would have failed in this scenario. Your suggesion is right but
I'll have to go through the tests to figure out the consequences.

Ajith

On 3/3/06, Chuck Williams <[EMAIL PROTECTED]> wrote:
  
WSDL2Java recurses infinitely in the SchemaCompiler when it encounters
recursion in the type definitions, e.g. for this single type:

    <xsd:schema targetNamespace="test">
      <xsd:element name="recursiveElement" type="recursive"/>
      <xsd:complexType name="recursive">
        <xsd:sequence>
          <xsd:element name="sub" type="test:recursive" minOccurs="0"/>
        </xsd:sequence>
      </xsd:complexType>
    </xsd:schema>

This definition validly describes a data strcuture like this one:

<recursive>
    <sub>
        <sub/>
    </sub>
</recursive>

The attached WSDL file is a simple standalone example that causes the
infinite recursion.

Is there any way to do this, or is this a bug in axis2, or am I somehow
confused?  It looks to me like the fix could be as simple as adding the
name of the complexType being processed to the processedTypemap in
SchemaCompiler.processNamedComplexSchemaType() before it is processed,
rather than waiting until after it is processed, but I'm not sure if
that woudl really work and what other consequences it might have.  There
seem to be similar issues with other recursion control mechanisms in
SchemaCompiler, e.g. in processElement().

Thanks for any help,

Chuck




    


--
Ajith Ranabahu
  

diff -urX exclude.files codegen.src.orig/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java codegen.src.new/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java
--- codegen.src.orig/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java	2006-01-24 18:14:07.000000000 -1000
+++ codegen.src.new/org/apache/axis2/schema/BeanWriterMetaInfoHolder.java	2006-02-08 22:24:45.000000000 -1000
@@ -32,6 +32,7 @@
 
 
     private boolean ordered = false;
+    private boolean choice = false;
     private boolean anonymous = false;
     private boolean extension = false;
     private String extensionClassName = "";
@@ -115,6 +116,24 @@
     public void setOrdered(boolean ordered) {
         this.ordered = ordered;
     }
+        
+    /**
+     * Gets the choice status.
+     *
+     * @return Returns boolean.
+     */
+    public boolean isChoice() {
+        return choice;
+    }
+
+    /**
+     * Sets the choice flag. 
+     *
+     * @param choice
+     */
+    public void setChoice(boolean choice) {
+        this.choice = choice;
+    }
 
     /**
      * Registers a mapping.
diff -urX exclude.files codegen.src.orig/org/apache/axis2/schema/SchemaCompiler.java codegen.src.new/org/apache/axis2/schema/SchemaCompiler.java
--- codegen.src.orig/org/apache/axis2/schema/SchemaCompiler.java	2006-01-24 18:14:07.000000000 -1000
+++ codegen.src.new/org/apache/axis2/schema/SchemaCompiler.java	2006-03-02 23:53:47.000000000 -1000
@@ -404,6 +404,10 @@
                 || baseSchemaTypeMap.containsKey(complexType.getQName())) {
             return;
         }
+        
+        // Must do this up front to support recursive types
+        String fullyQualifiedClassName = writer.makeFullyQualifiedClassName(complexType.getQName());
+        processedTypemap.put(complexType.getQName(), fullyQualifiedClassName);
 
         XmlSchemaParticle particle = complexType.getParticle();
         BeanWriterMetaInfoHolder metaInfHolder = new BeanWriterMetaInfoHolder();
@@ -440,10 +444,7 @@
 
         //write the class. This type mapping would have been populated right now
         //Note - We always write classes for complex types
-        String fullyQualifiedClassName = writer.write(complexType, processedTypemap, metaInfHolder);
-        //populate the type mapping with the elements
-        processedTypemap.put(complexType.getQName(), fullyQualifiedClassName);
-
+        writer.write(complexType, processedTypemap, metaInfHolder, fullyQualifiedClassName);
 
     }
 
@@ -497,7 +498,9 @@
             XmlSchemaObjectCollection items = ((XmlSchemaAll) particle).getItems();
             process(items, metainfHolder, false);
         } else if (particle instanceof XmlSchemaChoice) {
-            //put the code here to handle the choice!
+            XmlSchemaObjectCollection items = ((XmlSchemaChoice) particle).getItems();
+            metainfHolder.setChoice(true);
+            process(items, metainfHolder, false);
         }
     }
 
diff -urX exclude.files codegen.src.orig/org/apache/axis2/schema/template/ADBBeanTemplate.xsl codegen.src.new/org/apache/axis2/schema/template/ADBBeanTemplate.xsl
--- codegen.src.orig/org/apache/axis2/schema/template/ADBBeanTemplate.xsl	2006-01-24 18:14:07.000000000 -1000
+++ codegen.src.new/org/apache/axis2/schema/template/ADBBeanTemplate.xsl	2006-02-08 22:24:45.000000000 -1000
@@ -32,6 +32,8 @@
     <xsl:template match="bean">
 
         <xsl:variable name="name"><xsl:value-of select="@name"/></xsl:variable>
+        <xsl:variable name="choice" select="@choice"/>
+        
     <!-- write the class header. this should be done only when unwrapped -->
 
         <xsl:if test="not(not(@unwrapped) or (@skip-write))">
@@ -64,8 +66,18 @@
 
             </xsl:otherwise>
         </xsl:choose>
-
-
+        
+        <xsl:if test="$choice">
+            /** Whenever a new property is set ensure all others are unset
+             *  There can be only one choice and the last one wins
+             */
+            private void clearAllSettingTrackers() {
+            <xsl:for-each select="property">
+                local<xsl:value-of select="@javaname"/>Tracker = false;
+            </xsl:for-each>
+            }
+        </xsl:if>
+        
         <xsl:for-each select="property">
             <xsl:variable name="propertyType"><xsl:value-of select="@type"></xsl:value-of></xsl:variable>
             <xsl:variable name="propertyName"><xsl:value-of select="@name"></xsl:value-of></xsl:variable>
@@ -81,7 +93,7 @@
             private <xsl:value-of select="$propertyType"/><xsl:text> </xsl:text><xsl:value-of select="$varName" /> ;
             <!-- Generate a tracker only if the min occurs is zero, which means if the user does
                  not bother to set that value, we do not send it -->
-            <xsl:if test="$min=0">
+            <xsl:if test="($min=0) or $choice">
             /*  This tracker boolean wil be used to detect whether the user called the set method
                 for this attribute. It will be used to determine whether to include this field
                 in the serialized XML
@@ -115,10 +127,13 @@
                     }
                 </xsl:if>
             </xsl:if>
-             <xsl:if test="$min=0">
-             //update the setting tracker
-             <xsl:value-of select="$settingTracker"/> = true;
-             </xsl:if>
+            <xsl:if test="$choice">
+                clearAllSettingTrackers();
+            </xsl:if>
+            <xsl:if test="($min=0) or $choice">
+                //update the setting tracker
+                <xsl:value-of select="$settingTracker"/> = true;
+            </xsl:if>
             this.<xsl:value-of select="$varName"/>=param;
             }
         </xsl:for-each>
@@ -143,7 +158,7 @@
                     <xsl:variable name="settingTracker">local<xsl:value-of select="@javaname"/>Tracker</xsl:variable>
 
 
-                    <xsl:if test="$min=0"> if (<xsl:value-of select="$settingTracker"/>){</xsl:if>
+                    <xsl:if test="($min=0) or $choice"> if (<xsl:value-of select="$settingTracker"/>){</xsl:if>
                     <xsl:choose>
                         <xsl:when test="@ours or @any">
                             elementList.add(new javax.xml.namespace.QName("<xsl:value-of select="$namespace"/>",
@@ -160,7 +175,7 @@
                              elementList.add(org.apache.axis2.databinding.utils.ConverterUtil.convertToString(<xsl:value-of select="$varName"/>));
                         </xsl:otherwise>
                     </xsl:choose>
-                    <xsl:if test="$min=0">}</xsl:if>
+                    <xsl:if test="($min=0) or $choice">}</xsl:if>
                 </xsl:for-each>
 
                 <xsl:for-each select="[EMAIL PROTECTED]">
@@ -218,7 +233,10 @@
         try {
         int event = reader.getEventType();
         int count = 0;
-        int argumentCount = <xsl:value-of select="count(property)"/> ;
+        int argumentCount = <xsl:choose>
+                                <xsl:when test="$choice">1</xsl:when>
+                                <xsl:otherwise><xsl:value-of select="count(property)"/></xsl:otherwise>
+                            </xsl:choose>;
         boolean done =false;
         //event better be a START_ELEMENT. if not we should go up to the start element here
         while (!reader.isStartElement()){
diff -urX exclude.files codegen.src.orig/org/apache/axis2/schema/writer/BeanWriter.java codegen.src.new/org/apache/axis2/schema/writer/BeanWriter.java
--- codegen.src.orig/org/apache/axis2/schema/writer/BeanWriter.java	2006-01-24 18:14:07.000000000 -1000
+++ codegen.src.new/org/apache/axis2/schema/writer/BeanWriter.java	2006-03-02 23:54:38.000000000 -1000
@@ -1,5 +1,6 @@
 package org.apache.axis2.schema.writer;
 
+import javax.xml.namespace.QName;
 import org.apache.axis2.schema.BeanWriterMetaInfoHolder;
 import org.apache.axis2.schema.CompilerOptions;
 import org.apache.axis2.schema.SchemaCompilationException;
@@ -51,6 +52,12 @@
      * of the schema compiler may be exposed.
      */
     public Map getModelMap();
+    
+    /** Make the fully qualified class name for an element or named type
+     * @param qName the qualified Name for this element or type in the schema
+     * @return the appropriate fully qualified class name to use in generated code
+     */
+    public String makeFullyQualifiedClassName(QName qName);
 
     /**
      * Write a complex type
@@ -58,10 +65,11 @@
      * @param complexType
      * @param typeMap
      * @param metainf
+     * @param fullyQualifiedClassName the name returned by makeFullyQualifiedClassName() or null if it wasn't called
      * @return Returns String.
      * @throws SchemaCompilationException
      */
-    public String write(XmlSchemaComplexType complexType, Map typeMap, BeanWriterMetaInfoHolder metainf) throws SchemaCompilationException;
+    public String write(XmlSchemaComplexType complexType, Map typeMap, BeanWriterMetaInfoHolder metainf, String fullyQualifiedClassName) throws SchemaCompilationException;
 
     /**
      * Write a element
diff -urX exclude.files codegen.src.orig/org/apache/axis2/schema/writer/JavaBeanWriter.java codegen.src.new/org/apache/axis2/schema/writer/JavaBeanWriter.java
--- codegen.src.orig/org/apache/axis2/schema/writer/JavaBeanWriter.java	2006-01-24 18:14:07.000000000 -1000
+++ codegen.src.new/org/apache/axis2/schema/writer/JavaBeanWriter.java	2006-03-02 23:54:38.000000000 -1000
@@ -138,7 +138,7 @@
         try {
             QName qName = element.getQName();
 
-            return process(qName, metainf, typeMap, true);
+            return process(qName, metainf, typeMap, true, null);
         } catch (Exception e) {
             throw new SchemaCompilationException(e);
         }
@@ -150,16 +150,17 @@
      * @param complexType
      * @param typeMap
      * @param metainf
+     * @param fullyQualifiedClassName the name returned by makeFullyQualifiedClassName() or null if it wasn't called
      * @throws org.apache.axis2.schema.SchemaCompilationException
      *
      * @see BeanWriter#write(org.apache.ws.commons.schema.XmlSchemaComplexType, java.util.Map, org.apache.axis2.schema.BeanWriterMetaInfoHolder)
      */
-    public String write(XmlSchemaComplexType complexType, Map typeMap, BeanWriterMetaInfoHolder metainf) throws SchemaCompilationException {
+    public String write(XmlSchemaComplexType complexType, Map typeMap, BeanWriterMetaInfoHolder metainf, String fullyQualifiedClassName) throws SchemaCompilationException {
 
         try {
             //determine the package for this type.
             QName qName = complexType.getQName();
-            return process(qName, metainf, typeMap, false);
+            return process(qName, metainf, typeMap, false, fullyQualifiedClassName);
 
         } catch (SchemaCompilationException e) {
             throw e;
@@ -223,6 +224,31 @@
     }
 
 
+    /** Make the fully qualified class name for an element or named type
+     * @param qName the qualified Name for this element or type in the schema
+     * @return the appropriate fully qualified class name to use in generated code
+     */
+    public String makeFullyQualifiedClassName(QName qName) {
+        String nameSpaceFromURL = URLProcessor.makePackageName(qName.getNamespaceURI());
+        String packageName = this.packageName == null ?
+                nameSpaceFromURL :
+                this.packageName + nameSpaceFromURL;
+        String originalName = qName.getLocalPart();
+        String className = makeUniqueJavaClassName(this.namesList, originalName);
+        String packagePrefix = null;
+        String fullyqualifiedClassName;
+        if (wrapClasses)
+            packagePrefix =  (this.packageName == null ? DEFAULT_PACKAGE+"." : this.packageName) + WRAPPED_DATABINDING_CLASS_NAME;
+        else if (writeClasses)
+            packagePrefix = packageName;
+        if (packagePrefix!=null)
+            fullyqualifiedClassName = packagePrefix + (packagePrefix.endsWith(".")?"":".") + className;
+        else
+            fullyqualifiedClassName = className;
+        //return the fully qualified class name
+        return fullyqualifiedClassName;
+    }
+    
     /**
      * A util method that holds common code
      * for the complete schema that the generated XML complies to
@@ -235,7 +261,11 @@
      * @return Returns String.
      * @throws Exception
      */
-    private String process(QName qName, BeanWriterMetaInfoHolder metainf, Map typeMap, boolean isElement) throws Exception {
+    private String process(QName qName, BeanWriterMetaInfoHolder metainf, Map typeMap, boolean isElement, String fullyqualifiedClassName) throws Exception {
+        
+        if (fullyqualifiedClassName == null)
+            fullyqualifiedClassName = makeFullyQualifiedClassName(qName);
+        String className = fullyqualifiedClassName.substring(1+fullyqualifiedClassName.lastIndexOf('.'));
 
         String nameSpaceFromURL = URLProcessor.makePackageName(qName.getNamespaceURI());
 
@@ -244,11 +274,7 @@
                 this.packageName + nameSpaceFromURL;
 
         String originalName = qName.getLocalPart();
-        String className = makeUniqueJavaClassName(this.namesList, originalName);
-
-        String packagePrefix = null;
 
-        String fullyqualifiedClassName;
         ArrayList propertyNames = new ArrayList();
 
         if (!templateLoaded) {
@@ -261,7 +287,6 @@
             globalWrappedDocument.getDocumentElement().appendChild(
                     getBeanElement(globalWrappedDocument, className, originalName, packageName, qName, isElement, metainf, propertyNames, typeMap)
             );
-            packagePrefix =  (this.packageName == null ? DEFAULT_PACKAGE+"." : this.packageName) + WRAPPED_DATABINDING_CLASS_NAME;
 
         } else {
             //create the model
@@ -275,7 +300,6 @@
                 //parse with the template and create the files
                 parse(model, out);
 
-                packagePrefix = packageName ;
             }
 
             //add the model to the model map
@@ -286,11 +310,6 @@
 
         }
 
-        if (packagePrefix!=null){
-            fullyqualifiedClassName = packagePrefix + (packagePrefix.endsWith(".")?"":".") + className;
-        }else{
-            fullyqualifiedClassName = className;
-        }
         //return the fully qualified class name
         return fullyqualifiedClassName;
 
@@ -342,6 +361,11 @@
         if (metainf.isExtension()) {
             XSLTUtils.addAttribute(model, "extension", metainf.getExtensionClassName(), rootElt);
         }
+        
+        if (metainf.isChoice()) {
+            XSLTUtils.addAttribute(model, "choice", "yes", rootElt);
+        }
+        
         // go in the loop and add the part elements
         QName[] qNames;
         if (metainf.isOrdered()) {

Reply via email to