scheu 02/03/13 07:19:27 Modified: java/lib wsdl4j.jar java/src/org/apache/axis/wsdl/toJava Emitter.java JavaDeployWriter.java JavaStubWriter.java JavaWriterFactory.java SchemaUtils.java SymbolTable.java Utils.java java/test/wsdl/refattr refattr.wsdl Log: Changes for anonymous type processing in WSDL2Java Symbol Table. Problem Description: -------------------- An anonymous type is a type (simpleType or complexType) that does not have a name. Here is an example: ... <element name="foo"> <complexType> .... </complexType> The WSDL2Java symbol table uses the QName as the key to access a symbol table entry. Since an anonymous type does not have a qname, the qname of the containing element is used. Unfortunately it is possible that the element is not unique, which results in a symbol table collision. (As reported by Tom Jordahl.) Solution: --------- 1) The first change is to give anonymous type elements unique names to avoid symbol table collisions. The unique name must be something that can be determined by examining the dom tree. I chose to use the following format for the local name: <qname-of-containing-simple/compleType>.<qname-of-element> If the anonymous type is used to define a global element, its local name is: .<qname-of-element> 2) Change to the SchemaUtils code that queries the qname of an anon type. 3) Change to the JavaWriterFactory.javifyNames method. This method is enhanced to ensure that anonymous java type name collisions don't occur. If a name collision is detected, the suffix ANON### is appended to the name of the anonymous class java name to prevent the collision (### is a unique number). 4) Changed the refattr.wsdl to have a anonymous type collision similar to the one submitted to axis-dev by Tom Jordahl. Phase 2: -------- Anonymous types for root elements are not put into the symbol table. Instead the emitter writer classes use the DefinedElement information when generating code. This can lead to some fuzzy logic and subtle bugs. (I promised Glen last week that I would look at changing this code.) Solution: 1) Anonymous types for root elements are added to the symbol table just like all other anonymous types. 2) The JavaWriterFactory.resolve() method is modified to ensure that the java name of the root DefinedElement matches the java name of its anonymous DefinedType. 3) Changed Utils.getNested to get the anonymous DefinedType. 4) Changed the emitter writeTypes() method to only process Type entries (not Element entries). This is an improvement, writeTypes only deals with types! 5) Changed the deploy and stub writers to not register type mappings for Elements. (Before the deploy and stub writers had to examine the Element to see if it had an anonymous type...) * There are a number of existing testcases that use anonymous types for root elements. All of the tests passed. Revision Changes Path 1.10 +553 -414 xml-axis/java/lib/wsdl4j.jar <<Binary file>> 1.25 +3 -1 xml-axis/java/src/org/apache/axis/wsdl/toJava/Emitter.java Index: Emitter.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/wsdl/toJava/Emitter.java,v retrieving revision 1.24 retrieving revision 1.25 diff -u -r1.24 -r1.25 --- Emitter.java 5 Mar 2002 14:52:06 -0000 1.24 +++ Emitter.java 13 Mar 2002 15:19:26 -0000 1.25 @@ -482,9 +482,11 @@ // - we found its definition (getNode()) // - it is referenced // - it is not a base java type + // - it is a Type (not an Element) // (Note that types that are arrays are passed to getWriter // because they may require a Holder) - if (type.getNode() != null && + if (type.getNode() != null && + type instanceof Type && type.isReferenced() && type.getBaseType() == null) { Writer writer = writerFactory.getWriter(type, symbolTable); 1.26 +3 -13 xml-axis/java/src/org/apache/axis/wsdl/toJava/JavaDeployWriter.java Index: JavaDeployWriter.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/wsdl/toJava/JavaDeployWriter.java,v retrieving revision 1.25 retrieving revision 1.26 diff -u -r1.25 -r1.26 --- JavaDeployWriter.java 8 Mar 2002 20:04:45 -0000 1.25 +++ JavaDeployWriter.java 13 Mar 2002 15:19:26 -0000 1.26 @@ -157,28 +157,18 @@ // 1) Don't register types that are base (primitive) types. // If the baseType != null && getRefType() != null this // is a simpleType that must be registered. - // 2) Don't register the special types for collections - // (indexed properties) + // 2) Don't register the special types for collections + // (indexed properties) or element types // 3) Don't register types that are not referenced // or only referenced in a literal context. if ((type.getBaseType() != null && type.getRefType() == null) || type instanceof CollectionType || + type instanceof Element || !type.isReferenced() || type.isOnlyLiteralReferenced()) { process = false; } - - // 4) If the type is an element, the typemapping is only generated - // if the element has an anonymous type. This is a quick fix - // until I add anonymous types as actual symbol table elements. Scheu - if (process && type instanceof Element) { - Node node = symbolTable.getTypeEntry(type.getQName(), - true).getNode(); - if (node == null || - Utils.getNodeTypeRefQName(node, "type") != null) - process = false; - } if (process) { pw.println(" <typeMapping"); pw.println(" xmlns:ns=\"" + type.getQName().getNamespaceURI() + "\""); 1.42 +3 -12 xml-axis/java/src/org/apache/axis/wsdl/toJava/JavaStubWriter.java Index: JavaStubWriter.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/wsdl/toJava/JavaStubWriter.java,v retrieving revision 1.41 retrieving revision 1.42 diff -u -r1.41 -r1.42 --- JavaStubWriter.java 6 Mar 2002 18:50:51 -0000 1.41 +++ JavaStubWriter.java 13 Mar 2002 15:19:26 -0000 1.42 @@ -402,27 +402,18 @@ // 1) Don't register types that are base (primitive) types. // If the baseType != null && getRefType() != null this // is a simpleType that must be registered. - // 2) Don't register the special types for collections - // (indexed properties) + // 2) Don't register the special types for collections + // (indexed properties) or element types // 3) Don't register types that are not referenced // or only referenced in a literal context. if ((type.getBaseType() != null && type.getRefType() == null) || type instanceof CollectionType || + type instanceof Element || !type.isReferenced() || type.isOnlyLiteralReferenced()) { process = false; } - // 4) If the type is an element, the typemapping is only generated - // if the element has an anonymous type. This is a quick fix - // until I add anonymous types as actual symbol table elements. Scheu - if (process && type instanceof Element) { - Node node = symbolTable.getTypeEntry(type.getQName(), - true).getNode(); - if (node == null || - Utils.getNodeTypeRefQName(node, "type") != null) - process = false; - } if (!process) { return; } 1.17 +51 -4 xml-axis/java/src/org/apache/axis/wsdl/toJava/JavaWriterFactory.java Index: JavaWriterFactory.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/wsdl/toJava/JavaWriterFactory.java,v retrieving revision 1.16 retrieving revision 1.17 diff -u -r1.16 -r1.17 --- JavaWriterFactory.java 27 Feb 2002 13:41:28 -0000 1.16 +++ JavaWriterFactory.java 13 Mar 2002 15:19:27 -0000 1.17 @@ -157,17 +157,20 @@ } // getWriter /** - * Fill in the names of each SymTabEntry with the javaified name + * Fill in the names of each SymTabEntry with the javaified name. + * Note: This method also ensures that anonymous types are + * given unique java type names. */ private void javifyNames(SymbolTable symbolTable) { + int uniqueNum = 0; + HashMap anonQNames = new HashMap(); Iterator it = symbolTable.getHashMap().values().iterator(); while (it.hasNext()) { Vector v = (Vector) it.next(); for (int i = 0; i < v.size(); ++i) { SymTabEntry entry = (SymTabEntry) v.elementAt(i); - // If it's a type, then use the referenced type's QName to generate this type's - // name. + // Use the type or the referenced type's QName to generate the java name. if (entry instanceof TypeEntry) { TypeEntry tEntry = (TypeEntry) entry; String dims = tEntry.getDimensions(); @@ -177,7 +180,28 @@ dims += tEntry.getDimensions(); refType = tEntry.getRefType(); } - entry.setName(symbolTable.getJavaName(tEntry.getQName()) + dims); + // Get the QName to javify + QName typeQName = tEntry.getQName(); + if (typeQName.getLocalPart().lastIndexOf('.') >= 0) { + // This is an anonymous type name. + // Axis uses '.' as a nesting token to generate + // unique qnames for anonymous types. + // Only consider the localName after the last '.' when + // generating the java name + String localName = typeQName.getLocalPart(); + localName = localName.substring(localName.lastIndexOf('.')+1); + typeQName = new QName(typeQName.getNamespaceURI(), localName); + // If there is already an existing type, there will be a + // collision. If there is an existing anon type, there will be a + // collision. In both cases, the java type name should be mangled. + if (symbolTable.getType(typeQName) != null || + anonQNames.get(typeQName) != null) { + localName += "ANON" + uniqueNum++; + typeQName = new QName(typeQName.getNamespaceURI(), localName); + } + anonQNames.put(typeQName, typeQName); + } + entry.setName(symbolTable.getJavaName(typeQName) + dims); } // If it is not a type, then use this entry's QName to generate its name. @@ -245,6 +269,15 @@ if (entry instanceof Element) { entry.setName(mangleName(entry.getName(), "_ElemType")); + // If this global element was defined using + // an anonymous type, then need to change the + // java name of the anonymous type to match. + QName anonQName = new QName(entry.getQName().getNamespaceURI(), + "." + entry.getQName().getLocalPart()); + TypeEntry anonType = symbolTable.getType(anonQName); + if (anonType != null) { + anonType.setName(entry.getName()); + } } else if (entry instanceof TypeEntry) { // Search all other types for java names that match this one. @@ -508,6 +541,20 @@ p.type.setDynamicVar( JavaTypeWriter.HOLDER_IS_NEEDED, new Boolean(true)); + + // If the type is a DefinedElement, need to + // set HOLDER_IS_NEEDED on the anonymous type. + QName anonQName = SchemaUtils. + getElementAnonQName(p.type.getNode()); + if (anonQName != null) { + TypeEntry anonType = + symbolTable.getType(anonQName); + if (anonType != null) { + anonType.setDynamicVar( + JavaTypeWriter.HOLDER_IS_NEEDED, + new Boolean(true)); + } + } } } } 1.14 +27 -4 xml-axis/java/src/org/apache/axis/wsdl/toJava/SchemaUtils.java Index: SchemaUtils.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/wsdl/toJava/SchemaUtils.java,v retrieving revision 1.13 retrieving revision 1.14 diff -u -r1.13 -r1.14 --- SchemaUtils.java 11 Mar 2002 16:25:32 -0000 1.13 +++ SchemaUtils.java 13 Mar 2002 15:19:27 -0000 1.14 @@ -324,17 +324,40 @@ QName nodeName = Utils.getNodeNameQName(elementNode); BooleanHolder forElement = new BooleanHolder(); QName nodeType = Utils.getNodeTypeRefQName(elementNode, forElement); - if (nodeType == null) { // The element may use an anonymous type - nodeType = nodeName; - forElement.value = false; + if (nodeType == null) { // The element may use an anonymous type + nodeType = getElementAnonQName(elementNode); + forElement.value = false; } - + TypeEntry type = (TypeEntry) symbolTable.getTypeEntry(nodeType, forElement.value); if (type != null) { v.add(type); v.add(nodeName.getLocalPart()); } return v; + } + + /** + * Returns the WSDL2Java QName for the anonymous type of the element + * or null. + */ + public static QName getElementAnonQName(Node node) { + QName nodeKind = Utils.getNodeQName(node); + if (nodeKind != null && + nodeKind.getLocalPart().equals("element") && + Constants.isSchemaXSD(nodeKind.getNamespaceURI())) { + NodeList children = node.getChildNodes(); + for (int j = 0; j < children.getLength(); j++) { + QName kind = Utils.getNodeQName(children.item(j)); + if (kind != null && + (kind.getLocalPart().equals("complexType") || + kind.getLocalPart().equals("simpleType")) && + Constants.isSchemaXSD(kind.getNamespaceURI())) { + return Utils.getNodeNameQName(children.item(j)); + } + } + } + return null; } /** 1.43 +1 -14 xml-axis/java/src/org/apache/axis/wsdl/toJava/SymbolTable.java Index: SymbolTable.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/wsdl/toJava/SymbolTable.java,v retrieving revision 1.42 retrieving revision 1.43 diff -u -r1.42 -r1.43 --- SymbolTable.java 11 Mar 2002 16:25:32 -0000 1.42 +++ SymbolTable.java 13 Mar 2002 15:19:27 -0000 1.43 @@ -613,21 +613,8 @@ */ private void createTypeFromDef(Node node, boolean isElement, boolean belowSchemaLevel) throws IOException { - // See if this is an anonymous complexType for a global element - // If it is, the element in the dictionary is used. - QName qName = null; - TypeEntry anonType = null; - if (!isElement && - Utils.getAttribute(node, "name") == null) { - qName = Utils.getNodeNameQName(node); - if (qName != null && - getElement(qName) != null) { // Element exists in dictionary so use it. - return; - } - } - // Get the QName of the node's name attribute value - qName = Utils.getNodeNameQName(node); + QName qName = Utils.getNodeNameQName(node); if (qName != null) { // If the qname is already registered as a base type, 1.18 +27 -88 xml-axis/java/src/org/apache/axis/wsdl/toJava/Utils.java Index: Utils.java =================================================================== RCS file: /home/cvs/xml-axis/java/src/org/apache/axis/wsdl/toJava/Utils.java,v retrieving revision 1.17 retrieving revision 1.18 diff -u -r1.17 -r1.18 --- Utils.java 11 Mar 2002 16:25:32 -0000 1.17 +++ Utils.java 13 Mar 2002 15:19:27 -0000 1.18 @@ -97,93 +97,6 @@ return name; } // capitalizeFirstChar - - - /** - * Some QNames represent base types. This routine returns the - * name of the base java type or null. - * (These mappings based on JSR-101 version 0.6 Public Draft) - * ---------------------------------------------------------- - * Note that the Schema simple types map to different java types - * depending on whether the nillable flag is set. This routine - * assumes nillable is false. - * ---------------------------------------------------------- - * @param QName - */ - /* - public static String getBaseJavaName(QName qName) { - String localName = qName.getLocalPart(); - if (Constants.isSchemaXSD(qName.getNamespaceURI())) { - if (localName.equals("string")) { - return "java.lang.String"; - } else if (localName.equals("integer")) { - return "java.math.BigInteger"; - } else if (localName.equals("int")) { - return "int"; - } else if (localName.equals("long")) { - return "long"; - } else if (localName.equals("short")) { - return "short"; - } else if (localName.equals("decimal")) { - return "java.math.BigDecimal"; - } else if (localName.equals("float")) { - return "float"; - } else if (localName.equals("double")) { - return "double"; - } else if (localName.equals("boolean")) { - return "boolean"; - } else if (localName.equals("byte")) { - return "byte"; - } else if (localName.equals("QName")) { - return "javax.xml.rpc.namespace.QName"; - } else if (localName.equals("dateTime")) { - return "java.util.Date"; // Should be Calendar, but Calendar is abstract! - } else if (localName.equals("base64Binary")) { - return "byte[]"; - } else if (localName.equals("hexBinary")) { - return "byte[]"; - } else if (localName.equals("date")) { // Not defined in JSR-101 - return "java.util.Date"; - } else if (localName.equals("void")) { // Not defined in JSR-101 - return "void"; - } else if (localName.equals("anyType")) { - return "java.lang.Object"; - } - } - else if (Constants.isSOAP_ENC(qName.getNamespaceURI())) { - if (localName.equals("string")) { - return "java.lang.String"; - } else if (localName.equals("int")) { - return "java.lang.Integer"; - } else if (localName.equals("short")) { - return "java.lang.Short"; - } else if (localName.equals("decimal")) { - return "java.math.BigDecimal"; - } else if (localName.equals("float")) { - return "java.lang.Float"; - } else if (localName.equals("double")) { - return "java.lang.Double"; - } else if (localName.equals("boolean")) { - return "java.lang.Boolean"; - } else if (localName.equals("base64")) { - return "java.lang.Byte[]"; - } else if (localName.equals("byte")) { - return "java.lang.Byte"; - } else if (localName.equals("Array")) { // Support for JAX-RPC Array - return "Object[]"; - } else if (localName.equals("Vector")) { // Not defined in JSR-101 - return "java.util.Vector"; - } - } - // special "java" namesapce means straight java types - // So "java:void" maps to "void" - else if (qName.getNamespaceURI().equals("java")) { // Not defined in JSR-101 - return localName; - } - return null; - } - */ - /** * getNillableQName returns the QName to use if the nillable=true * attribute is used. @@ -327,7 +240,23 @@ // the complexType may be anonymous, which is why the getScopedAttribute // method is used. if (localName == null) { - localName = getScopedAttribute(node, "name"); + localName = ""; + Node search = node.getParentNode(); + while(search != null) { + QName kind = getNodeQName(search); + if (kind.getLocalPart().equals("schema")) { + search = null; + } else if (kind.getLocalPart().equals("element")) { + localName = "." + getNodeNameQName(search).getLocalPart(); + search = search.getParentNode(); + } else if (kind.getLocalPart().equals("complexType") || + kind.getLocalPart().equals("simpleType")) { + localName = getNodeNameQName(search).getLocalPart() + localName; + search = null; + } else { + search = search.getParentNode(); + } + } } if (localName == null) return null; @@ -707,6 +636,16 @@ } } } + + // Get the anonymous type of the element + QName anonQName = SchemaUtils.getElementAnonQName(type); + if (anonQName != null) { + TypeEntry anonType = symbolTable.getType(anonQName); + if (anonType != null && !types.contains(anonType)) { + types.add(anonType); + } + } + // Process extended types TypeEntry extendType = SchemaUtils.getComplexElementExtensionBase(type, symbolTable); if (extendType != null) { 1.4 +21 -1 xml-axis/java/test/wsdl/refattr/refattr.wsdl Index: refattr.wsdl =================================================================== RCS file: /home/cvs/xml-axis/java/test/wsdl/refattr/refattr.wsdl,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- refattr.wsdl 25 Jan 2002 18:31:53 -0000 1.3 +++ refattr.wsdl 13 Mar 2002 15:19:27 -0000 1.4 @@ -28,13 +28,33 @@ <xsd:all> <xsd:element name="areaCode" type="xsd:int"/> <xsd:element name="exchange" type="xsd:string"/> - <xsd:element name="number" type="xsd:string"/> + <xsd:element name="number"> + <xsd:complexType> + <xsd:simpleContent> + <xsd:extension base = "xsd:string"> + <xsd:attribute name= "length" type="xsd:int" /> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + </xsd:element> </xsd:all> </xsd:complexType> <xsd:complexType name="example"> <xsd:all> <xsd:element name="address" type="soapenc:string"/> + <!-- both example and phone contain anonymous types of number elements. --> + <!-- This should result in two Number classes...one which has a mangled name --> + <xsd:element name="number"> + <xsd:complexType> + <xsd:simpleContent> + <xsd:extension base = "xsd:string"> + <xsd:attribute name= "theLength" type="xsd:int" /> + </xsd:extension> + </xsd:simpleContent> + </xsd:complexType> + </xsd:element> + </xsd:all> </xsd:complexType>