This is an automated email from the ASF dual-hosted git repository. coheigea pushed a commit to branch coheigea/misc in repository https://gitbox.apache.org/repos/asf/ws-xmlschema.git
commit 7145f9281b7a6bdd911765eca407e26de39a9c3f Author: Colm O hEigeartaigh <[email protected]> AuthorDate: Thu Sep 24 11:13:18 2026 +0100 Misc fixes --- .../schema/docpath/XmlSchemaElementValidator.java | 8 ++ .../ws/commons/schema/walker/XmlSchemaScope.java | 23 +++- .../schema/docpath/TestPathFinderFailureTypes.java | 11 ++ .../walker/ComplexContentExtensionWalkerTest.java | 129 +++++++++++++++++++++ 4 files changed, 168 insertions(+), 3 deletions(-) diff --git a/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java b/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java index 11e2c836..3efeaa57 100644 --- a/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java +++ b/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java @@ -144,6 +144,14 @@ final class XmlSchemaElementValidator { continue; } + /* + * An attribute declared without a type is an xs:anySimpleType, + * which accepts any value; the walker leaves its type unset. + */ + if (attribute.getType() == null) { + continue; + } + if (attribute.getType().getType().equals(XmlSchemaTypeInfo.Type.COMPLEX)) { throw new ValidationException("Attribute " + attrQName + " of element " + elemQName diff --git a/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java b/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java index 1fcb5592..08be6e43 100644 --- a/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java +++ b/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java @@ -403,6 +403,14 @@ final class XmlSchemaScope { child = baseParticle; } else if (baseParticle == null) { child = ext.getParticle(); + } else if (!(baseParticle instanceof XmlSchemaSequenceMember) + || !(ext.getParticle() instanceof XmlSchemaSequenceMember)) { + // Only an xs:all is not a sequence member, and XML Schema 1.0 + // does not allow one to be combined with any other particle. + throw new XmlSchemaException("An extension of " + ext.getBaseTypeName() + + " adds content to it, but one of the two content" + + " models is an xs:all group, which cannot be" + + " combined with other particles."); } else { XmlSchemaSequence seq = new XmlSchemaSequence(); seq.getItems().add((XmlSchemaSequenceMember)baseParticle); @@ -420,8 +428,13 @@ final class XmlSchemaScope { } else if (ext.getAnyAttribute() == null) { anyAttr = baseAnyAttr; } else { - String[] baseNamespaces = baseAnyAttr.getNamespace().split(" "); - String[] childNamespaces = ext.getAnyAttribute().getNamespace().split(" "); + // An absent namespace attribute means ##any, and a union with + // ##any is ##any. + final String baseNamespace = baseAnyAttr.getNamespace(); + final String childNamespace = ext.getAnyAttribute().getNamespace(); + final boolean unionIsAny = isAnyNamespace(baseNamespace) || isAnyNamespace(childNamespace); + String[] baseNamespaces = unionIsAny ? new String[0] : baseNamespace.split(" "); + String[] childNamespaces = unionIsAny ? new String[0] : childNamespace.split(" "); HashSet<String> namespaces = new HashSet<String>(); for (String baseNs : baseNamespaces) { @@ -441,7 +454,7 @@ final class XmlSchemaScope { } anyAttr = new XmlSchemaAnyAttribute(); - anyAttr.setNamespace(nsAsString.toString()); + anyAttr.setNamespace(unionIsAny ? "##any" : nsAsString.toString()); anyAttr.setProcessContent(ext.getAnyAttribute().getProcessContent()); anyAttr.setAnnotation(ext.getAnyAttribute().getAnnotation()); anyAttr.setId(ext.getAnyAttribute().getId()); @@ -825,6 +838,10 @@ final class XmlSchemaScope { return (parent == null) ? null : parent.getUserRecognizedType(); } + private static boolean isAnyNamespace(String namespace) { + return (namespace == null) || "##any".equals(namespace.trim()); + } + private static String getName(XmlSchemaNamed name, String defaultName) { if (name.isAnonymous()) { return defaultName; diff --git a/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestPathFinderFailureTypes.java b/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestPathFinderFailureTypes.java index 7aaec5c0..06a96cda 100644 --- a/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestPathFinderFailureTypes.java +++ b/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestPathFinderFailureTypes.java @@ -136,6 +136,17 @@ public class TestPathFinderFailureTypes extends Assert { } } + /** + * An attribute declared without a type is an xs:anySimpleType and accepts any value. + */ + @Test + public void testUntypedAttributeAcceptsAnyValue() throws Exception { + walk("<xs:element name=\"root\"><xs:complexType>" + + "<xs:attribute name=\"x\"/>" + + "</xs:complexType></xs:element>", + "<root xmlns=\"urn:t\" x=\"1\"/>"); + } + @Test public void testValidDocumentStillWalks() throws Exception { walk("<xs:simpleType name=\"s\"><xs:restriction base=\"xs:string\">" diff --git a/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/ComplexContentExtensionWalkerTest.java b/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/ComplexContentExtensionWalkerTest.java new file mode 100644 index 00000000..2d9faecf --- /dev/null +++ b/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/ComplexContentExtensionWalkerTest.java @@ -0,0 +1,129 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.ws.commons.schema.walker; + +import java.io.StringReader; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.xml.namespace.QName; + +import org.apache.ws.commons.schema.XmlSchemaAnyAttribute; +import org.apache.ws.commons.schema.XmlSchemaCollection; +import org.apache.ws.commons.schema.XmlSchemaElement; +import org.apache.ws.commons.schema.XmlSchemaException; + +import org.junit.Assert; +import org.junit.Test; + +/** + * A complex content extension combines the content model and attribute wildcard of its base + * with its own. Doing so must not escape as a ClassCastException or NullPointerException. + */ +public class ComplexContentExtensionWalkerTest extends Assert { + + private static List<String> walkRoot(String body) { + XmlSchemaCollection collection = new XmlSchemaCollection(); + collection.read(new StringReader( + "<xs:schema xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns:tns=\"urn:ext\"" + + " targetNamespace=\"urn:ext\" elementFormDefault=\"qualified\">" + + body + + "</xs:schema>")); + XmlSchemaElement root = collection.getElementByQName(new QName("urn:ext", "root")); + assertNotNull("the schema under test must declare a 'root' element", root); + + // Records the namespace of every attribute wildcard visited. + final List<String> anyAttributeNamespaces = new ArrayList<String>(); + XmlSchemaVisitor visitor = (XmlSchemaVisitor)Proxy.newProxyInstance( + XmlSchemaVisitor.class.getClassLoader(), new Class<?>[] {XmlSchemaVisitor.class}, + (proxy, method, args) -> { + if ("onVisitAnyAttribute".equals(method.getName())) { + anyAttributeNamespaces.add(((XmlSchemaAnyAttribute)args[1]).getNamespace()); + } + return null; + }); + new XmlSchemaWalker(collection, visitor).walk(root); + return anyAttributeNamespaces; + } + + @Test + public void testExtendingAnAllGroupWithMoreContentIsRejected() { + try { + walkRoot("<xs:complexType name=\"base\"><xs:all>" + + "<xs:element name=\"a\" type=\"xs:string\"/>" + + "</xs:all></xs:complexType>" + + "<xs:complexType name=\"derived\"><xs:complexContent>" + + "<xs:extension base=\"tns:base\"><xs:sequence>" + + "<xs:element name=\"b\" type=\"xs:string\"/>" + + "</xs:sequence></xs:extension></xs:complexContent></xs:complexType>" + + "<xs:element name=\"root\" type=\"tns:derived\"/>"); + fail("expected an xs:all combined with other content to be rejected"); + } catch (XmlSchemaException expected) { + assertTrue(expected.getMessage(), expected.getMessage().contains("xs:all")); + } + } + + @Test + public void testExtendingASequenceWithASequenceStillWalks() { + walkRoot("<xs:complexType name=\"base\"><xs:sequence>" + + "<xs:element name=\"a\" type=\"xs:string\"/>" + + "</xs:sequence></xs:complexType>" + + "<xs:complexType name=\"derived\"><xs:complexContent>" + + "<xs:extension base=\"tns:base\"><xs:sequence>" + + "<xs:element name=\"b\" type=\"xs:string\"/>" + + "</xs:sequence></xs:extension></xs:complexContent></xs:complexType>" + + "<xs:element name=\"root\" type=\"tns:derived\"/>"); + } + + /** + * An xs:anyAttribute with no namespace attribute allows any namespace, and so does its union + * with any other wildcard. + */ + @Test + public void testAnyAttributeWithoutANamespaceIsAnyNamespace() { + List<String> namespaces = + walkRoot("<xs:complexType name=\"base\"><xs:sequence/>" + + "<xs:anyAttribute/></xs:complexType>" + + "<xs:complexType name=\"derived\"><xs:complexContent>" + + "<xs:extension base=\"tns:base\">" + + "<xs:anyAttribute namespace=\"urn:other\" processContents=\"lax\"/>" + + "</xs:extension></xs:complexContent></xs:complexType>" + + "<xs:element name=\"root\" type=\"tns:derived\"/>"); + assertEquals(1, namespaces.size()); + assertEquals("##any", namespaces.get(0)); + } + + @Test + public void testAnyAttributeNamespacesAreStillUnited() { + List<String> namespaces = + walkRoot("<xs:complexType name=\"base\"><xs:sequence/>" + + "<xs:anyAttribute namespace=\"urn:a\"/></xs:complexType>" + + "<xs:complexType name=\"derived\"><xs:complexContent>" + + "<xs:extension base=\"tns:base\">" + + "<xs:anyAttribute namespace=\"urn:b\"/>" + + "</xs:extension></xs:complexContent></xs:complexType>" + + "<xs:element name=\"root\" type=\"tns:derived\"/>"); + assertEquals(1, namespaces.size()); + String[] united = namespaces.get(0).trim().split(" "); + Arrays.sort(united); + assertArrayEquals(new String[] {"urn:a", "urn:b"}, united); + } +}
