You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ws.apache.org by dk...@apache.org on 2014/09/17 17:32:46 UTC

svn commit: r1625632 [1/9] - in /webservices/xmlschema/trunk: ./ xmlschema-walker/ xmlschema-walker/src/ xmlschema-walker/src/main/ xmlschema-walker/src/main/java/ xmlschema-walker/src/main/java/org/ xmlschema-walker/src/main/java/org/apache/ xmlschema...

Author: dkulp
Date: Wed Sep 17 15:32:44 2014
New Revision: 1625632

URL: http://svn.apache.org/r1625632
Log:
[XMLSCHEMA-36] Add the xmlschema-walker code from Michael Pigott
Patch applied, reformatted, minor API updates

Added:
    webservices/xmlschema/trunk/xmlschema-walker/   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/pom.xml   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/DomBuilderFromSax.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/SaxWalkerOverDom.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaDocumentNode.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaNamespaceContext.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaPathFinder.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaPathManager.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaPathNode.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaStateMachineGenerator.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaStateMachineNode.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/package-info.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaAttrInfo.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaBaseSimpleType.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaRestriction.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaScope.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaTypeInfo.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaVisitor.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/XmlSchemaWalker.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/walker/package-info.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/ExpectedElement.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/ExpectedNode.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/ExpectedPathNode.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/ExpectedStateMachineNode.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestDomBuilderFromSax.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestSaxWalkerOverDom.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestXmlSchemaElementValidator.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestXmlSchemaNamespaceContext.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestXmlSchemaPathFinder.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/docpath/TestXmlSchemaStateMachineGenerator.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/testutils/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/testutils/UtilsForTests.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/TestSchemaWalker.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/TestXmlSchemaBaseSimpleType.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/java/org/apache/ws/commons/schema/walker/TestXmlSchemaRestriction.java   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/resources/
    webservices/xmlschema/trunk/xmlschema-walker/src/test/resources/complex_schema.xsd   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/resources/complex_test1.xml   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/resources/test1_root.xml   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/resources/test2_children.xml   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/resources/test3_grandchildren.xml   (with props)
    webservices/xmlschema/trunk/xmlschema-walker/src/test/resources/test_schema.xsd   (with props)
Modified:
    webservices/xmlschema/trunk/pom.xml

Modified: webservices/xmlschema/trunk/pom.xml
URL: http://svn.apache.org/viewvc/webservices/xmlschema/trunk/pom.xml?rev=1625632&r1=1625631&r2=1625632&view=diff
==============================================================================
--- webservices/xmlschema/trunk/pom.xml (original)
+++ webservices/xmlschema/trunk/pom.xml Wed Sep 17 15:32:44 2014
@@ -84,6 +84,7 @@
         <module>xmlschema-core</module>
         <module>xmlschema-bundle-test</module>
         <module>w3c-testcases</module>
+        <module>xmlschema-walker</module>
     </modules>
     <build>
         <defaultGoal>install</defaultGoal>

Propchange: webservices/xmlschema/trunk/xmlschema-walker/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Wed Sep 17 15:32:44 2014
@@ -0,0 +1,10 @@
+build
+target
+.settings
+.classpath
+.project
+site
+.pmd
+.ruleset
+.checkstyle
+

Added: webservices/xmlschema/trunk/xmlschema-walker/pom.xml
URL: http://svn.apache.org/viewvc/webservices/xmlschema/trunk/xmlschema-walker/pom.xml?rev=1625632&view=auto
==============================================================================
--- webservices/xmlschema/trunk/xmlschema-walker/pom.xml (added)
+++ webservices/xmlschema/trunk/xmlschema-walker/pom.xml Wed Sep 17 15:32:44 2014
@@ -0,0 +1,29 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <groupId>org.apache.ws.xmlschema</groupId>
+    <artifactId>xmlschema</artifactId>
+    <version>2.2.0-SNAPSHOT</version>
+  </parent>
+  <artifactId>xmlschema-walker</artifactId>
+  <name>xmlschema-walker</name>
+  <description>Code to walk an XML Schema and confirm an XML Document conforms to it.</description>
+  <packaging>jar</packaging>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.ws.xmlschema</groupId>
+      <artifactId>xmlschema-core</artifactId>
+      <version>${project.version}</version>
+    </dependency>
+    <dependency>
+      <groupId>junit</groupId>
+      <artifactId>junit</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>xmlunit</groupId>
+      <artifactId>xmlunit</artifactId>
+      <scope>test</scope>
+    </dependency>
+  </dependencies>
+</project>
\ No newline at end of file

Propchange: webservices/xmlschema/trunk/xmlschema-walker/pom.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: webservices/xmlschema/trunk/xmlschema-walker/pom.xml
------------------------------------------------------------------------------
    svn:mime-type = text/xml

Added: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/DomBuilderFromSax.java
URL: http://svn.apache.org/viewvc/webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/DomBuilderFromSax.java?rev=1625632&view=auto
==============================================================================
--- webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/DomBuilderFromSax.java (added)
+++ webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/DomBuilderFromSax.java Wed Sep 17 15:32:44 2014
@@ -0,0 +1,390 @@
+/**
+ * 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.docpath;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.ws.commons.schema.XmlSchemaCollection;
+import org.apache.ws.commons.schema.XmlSchemaElement;
+import org.apache.ws.commons.schema.constants.Constants;
+import org.apache.ws.commons.schema.walker.XmlSchemaAttrInfo;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Builds an XML {@link org.w3c.dom.Document} from an XML Schema during a SAX
+ * walk.
+ */
+public final class DomBuilderFromSax extends DefaultHandler {
+
+    private static final String XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
+    private static final String XSI_SCHEMALOC = "schemaLocation";
+    private static final String XSI_NIL = "nil";
+
+    private Document document;
+    private StringBuilder content;
+    private Map<String, String> namespaceToLocationMapping;
+    private List<String> newPrefixes;
+    private XmlSchemaNamespaceContext nsContext;
+
+    private Map<QName, XmlSchemaStateMachineNode> elementsByQName;
+
+    private final ArrayList<Element> elementStack;
+    private final DocumentBuilder docBuilder;
+    private final XmlSchemaCollection schemas;
+    private final Set<String> globalNamespaces;
+
+    /**
+     * Creates a new <code>DocumentBuilderFromSax</code>.
+     *
+     * @throws ParserConfigurationException If unable to create a
+     *             {@link DocumentBuilder}.
+     */
+    public DomBuilderFromSax(XmlSchemaCollection xmlSchemaCollection) throws ParserConfigurationException {
+
+        if (xmlSchemaCollection == null) {
+            throw new IllegalArgumentException("xmlSchemaCollection cannot be null.");
+        }
+
+        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+        factory.setNamespaceAware(true);
+
+        docBuilder = factory.newDocumentBuilder();
+        elementStack = new ArrayList<Element>();
+        newPrefixes = new ArrayList<String>();
+        nsContext = new XmlSchemaNamespaceContext();
+
+        document = null;
+        content = null;
+        namespaceToLocationMapping = null;
+        elementsByQName = null;
+        schemas = xmlSchemaCollection;
+
+        globalNamespaces = new HashSet<String>();
+        globalNamespaces.add("http://www.w3.org/2001/XMLSchema-instance");
+    }
+
+    /**
+     * @see org.xml.sax.helpers.DefaultHandler#startDocument()
+     */
+    @Override
+    public void startDocument() throws SAXException {
+        document = docBuilder.newDocument();
+        document.setXmlStandalone(true);
+    }
+
+    /**
+     * @see org.xml.sax.helpers.DefaultHandler#startPrefixMapping(String,
+     *      String)
+     */
+    @Override
+    public void startPrefixMapping(String prefix, String uri) throws SAXException {
+
+        nsContext.addNamespace(prefix, uri);
+        newPrefixes.add(prefix);
+    }
+
+    /**
+     * @see org.xml.sax.helpers.DefaultHandler#endPrefixMapping(String)
+     */
+    @Override
+    public void endPrefixMapping(String prefix) throws SAXException {
+        nsContext.removeNamespace(prefix);
+    }
+
+    /**
+     * Starts a new element in the generated XML document. If the previous
+     * element was not closed, adds this element as a child to that element.
+     *
+     * @see DefaultHandler#startElement(String, String, String, Attributes)
+     */
+    @Override
+    public void startElement(String uri, String localName, String qName, Attributes atts) throws SAXException {
+
+        addContentToCurrentElement(false);
+
+        final Element element = document.createElementNS((uri.length() == 0) ? null : uri, qName);
+
+        // Define New Prefixes
+        for (String newPrefix : newPrefixes) {
+            final String namespace = nsContext.getNamespaceURI(newPrefix);
+            if (namespace == null) {
+                throw new SAXException("Prefix " + newPrefix + " is not recognized.");
+            }
+            String qualifiedName = null;
+            if (newPrefix.length() > 0) {
+                qualifiedName = Constants.XMLNS_ATTRIBUTE + ':' + newPrefix;
+            } else {
+                qualifiedName = Constants.XMLNS_ATTRIBUTE;
+            }
+
+            try {
+                element.setAttributeNS(Constants.XMLNS_ATTRIBUTE_NS_URI, qualifiedName, namespace);
+            } catch (DOMException e) {
+                throw new IllegalStateException("Cannot add namespace attribute ns=\""
+                                                + Constants.XMLNS_ATTRIBUTE_NS_URI + "\", qn=\""
+                                                + qualifiedName + "\", value=\"" + namespace
+                                                + "\" to element \"" + qName + "\".", e);
+            }
+        }
+        newPrefixes.clear();
+
+        // Add Attributes
+        final QName elemQName = new QName(uri, localName);
+        XmlSchemaStateMachineNode stateMachine = null;
+        if (elementsByQName != null) {
+            stateMachine = elementsByQName.get(elemQName);
+        }
+
+        for (int attrIndex = 0; attrIndex < atts.getLength(); ++attrIndex) {
+            String attrUri = atts.getURI(attrIndex);
+            if (attrUri.length() == 0) {
+                attrUri = null;
+            }
+
+            boolean isGlobal = globalNamespaces.contains(attrUri);
+            if ((attrUri != null) && !isGlobal) {
+                final QName attrQName = new QName(attrUri, atts.getLocalName(attrIndex));
+
+                boolean found = false;
+                if (stateMachine != null) {
+                    for (XmlSchemaAttrInfo attrInfo : stateMachine.getAttributes()) {
+                        if (attrInfo.getAttribute().getQName().equals(attrQName)) {
+                            found = true;
+                            isGlobal = attrInfo.isTopLevel();
+                        }
+                    }
+                }
+
+                if (!found && (schemas.getAttributeByQName(attrQName) != null)) {
+                    isGlobal = true;
+                }
+            }
+
+            final String attrValue = atts.getValue(attrIndex);
+
+            if (!isGlobal) {
+                element.setAttribute(atts.getLocalName(attrIndex), attrValue);
+            } else {
+                element.setAttributeNS(attrUri, atts.getQName(attrIndex), attrValue);
+            }
+
+        }
+
+        // Update the Parent Element
+        if (!elementStack.isEmpty()) {
+            elementStack.get(elementStack.size() - 1).appendChild(element);
+        } else {
+            addNamespaceLocationMappings(element);
+            document.appendChild(element);
+        }
+
+        elementStack.add(element);
+    }
+
+    /**
+     * Adds content to the current element.
+     *
+     * @see DefaultHandler#characters(char[], int, int)
+     */
+    @Override
+    public void characters(char[] ch, int start, int length) throws SAXException {
+
+        if (content == null) {
+            content = new StringBuilder();
+        }
+        content.append(ch, start, length);
+    }
+
+    /**
+     * Closes the current element in the generated XML document.
+     *
+     * @see DefaultHandler#endElement(String, String, String)
+     */
+    @Override
+    public void endElement(String uri, String localName, String qName) throws SAXException {
+
+        addContentToCurrentElement(true);
+
+        if (elementStack.isEmpty()) {
+            StringBuilder errMsg = new StringBuilder("Attempted to end element {");
+            errMsg.append(uri).append('}').append(localName);
+            errMsg.append(", but the stack is empty!");
+            throw new IllegalStateException(errMsg.toString());
+        }
+
+        final Element element = elementStack.remove(elementStack.size() - 1);
+
+        final String ns = (uri.length() == 0) ? null : uri;
+
+        final boolean namespacesMatch = (((ns == null) && (element.getNamespaceURI() == null)) || ((ns != null) && ns
+            .equals(element.getNamespaceURI())));
+
+        if (!namespacesMatch || !element.getLocalName().equals(localName)) {
+            StringBuilder errMsg = new StringBuilder("Attempted to end element {");
+            errMsg.append(ns).append('}').append(localName).append(", but found {");
+            errMsg.append(element.getNamespaceURI()).append('}');
+            errMsg.append(element.getLocalName()).append(" on the stack instead!");
+            throw new IllegalStateException(errMsg.toString());
+        }
+    }
+
+    /**
+     * @see org.xml.sax.helpers.DefaultHandler#endDocument()
+     */
+    @Override
+    public void endDocument() throws SAXException {
+        if (!elementStack.isEmpty()) {
+            StringBuilder errMsg = new StringBuilder("Ending an XML document with ");
+            errMsg.append(elementStack.size()).append(" elements still open.");
+
+            elementStack.clear();
+
+            throw new IllegalStateException(errMsg.toString());
+        }
+    }
+
+    private void addContentToCurrentElement(boolean isEnd) {
+        if ((content == null) || (content.length() == 0)) {
+            /*
+             * If we reached the end of the element, check if we received any
+             * content. If not, and if the element is nillable, write a nil
+             * attribute.
+             */
+            if (isEnd && !elementStack.isEmpty() && (schemas != null)) {
+                final Element currElem = elementStack.get(elementStack.size() - 1);
+                if (currElem.getChildNodes().getLength() == 0) {
+                    final QName elemQName = new QName(currElem.getNamespaceURI(), currElem.getLocalName());
+
+                    XmlSchemaElement schemaElem = null;
+
+                    if (elementsByQName != null) {
+                        final XmlSchemaStateMachineNode stateMachine = elementsByQName.get(elemQName);
+                        if ((stateMachine != null)
+                            && stateMachine.getNodeType().equals(XmlSchemaStateMachineNode.Type.ELEMENT)) {
+                            schemaElem = stateMachine.getElement();
+                        }
+                    }
+
+                    if (schemaElem == null) {
+                        schemaElem = schemas.getElementByQName(elemQName);
+                    }
+
+                    if ((schemaElem != null) && schemaElem.isNillable()) {
+                        currElem.setAttributeNS(XSI_NS, XSI_NIL, "true");
+                    }
+                }
+            }
+            return;
+        }
+
+        if (elementStack.isEmpty()) {
+            StringBuilder errMsg = new StringBuilder("Attempted to add content \"");
+            errMsg.append(content.toString()).append("\", but there were no ");
+            errMsg.append("elements in the stack!");
+            throw new IllegalStateException(errMsg.toString());
+        }
+
+        elementStack.get(elementStack.size() - 1).appendChild(document.createTextNode(content.toString()));
+
+        content.delete(0, content.length());
+    }
+
+    /**
+     * Retrieves the document constructed from the SAX walk.
+     */
+    public Document getDocument() {
+        return document;
+    }
+
+    /**
+     * Retrieves the XML Schema namespace -> location mapping set by the last
+     * call to {@link #setNamespaceToLocationMapping(Map)}.
+     */
+    public Map<String, String> getNamespaceToLocationMapping() {
+        return namespaceToLocationMapping;
+    }
+
+    /**
+     * Sets the XML Schema namespace -> location mapping to use when defining
+     * the schemaLocation attribute in the generated XML document.
+     *
+     * @param nsToLocMapping The namespace -> location mapping.
+     */
+    public void setNamespaceToLocationMapping(Map<String, String> nsToLocMapping) {
+        namespaceToLocationMapping = nsToLocMapping;
+    }
+
+    /**
+     * Retrieves the {@link QName} -> {@link XmlSchemaStateMachineNode} mapping
+     * defined by the call to {@link #setStateMachinesByQName(Map)}.
+     */
+    public Map<QName, XmlSchemaStateMachineNode> getStateMachinesByQName() {
+        return elementsByQName;
+    }
+
+    /**
+     * Sets the mapping of {@link QName}s to {@link XmlSchemaStateMachineNode}s.
+     * This is used to disambiguate:
+     * <ul>
+     * <li>Whether empty content indicates nil</li>
+     * <li>If an element's attribute requires a namespace</li>
+     * </ul>
+     *
+     * @param statesByQName The state-machine-node-by-QName mapping.
+     */
+    public void setStateMachinesByQName(Map<QName, XmlSchemaStateMachineNode> statesByQName) {
+        elementsByQName = statesByQName;
+    }
+
+    private void addNamespaceLocationMappings(Element rootElement) {
+        if ((namespaceToLocationMapping == null) || namespaceToLocationMapping.isEmpty()
+            || rootElement.hasAttributeNS(XSI_NS, XSI_SCHEMALOC)) {
+
+            /*
+             * There are no namesapces mappings to add, or a namespace mapping
+             * already exists.
+             */
+            return;
+        }
+
+        StringBuilder schemaList = new StringBuilder();
+        for (Map.Entry<String, String> e : namespaceToLocationMapping.entrySet()) {
+            schemaList.append(e.getKey()).append(' ').append(e.getValue());
+            schemaList.append(' ');
+        }
+        schemaList.delete(schemaList.length() - 1, schemaList.length());
+
+        rootElement.setAttributeNS(XSI_NS, XSI_SCHEMALOC, schemaList.toString());
+    }
+}

Propchange: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/DomBuilderFromSax.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/SaxWalkerOverDom.java
URL: http://svn.apache.org/viewvc/webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/SaxWalkerOverDom.java?rev=1625632&view=auto
==============================================================================
--- webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/SaxWalkerOverDom.java (added)
+++ webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/SaxWalkerOverDom.java Wed Sep 17 15:32:44 2014
@@ -0,0 +1,434 @@
+/**
+ * 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.docpath;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.namespace.QName;
+
+import org.apache.ws.commons.schema.constants.Constants;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+/**
+ * Walks over a {@link Document} in a SAX style, notifying listeners with SAX
+ * events.
+ * <p>
+ * Because the document has already been processed, only the following methods
+ * in the {@link ContentHandler} will be called:
+ * <ul>
+ * <li>{@link ContentHandler#startDocument()}</li>
+ * <li>{@link ContentHandler#startPrefixMapping(String, String)}</li>
+ * <li>{@link ContentHandler#startElement(String, String, String, Attributes)}</li>
+ * <li>{@link ContentHandler#characters(char[], int, int)}</li>
+ * <li>{@link ContentHandler#endElement(String, String, String)}</li>
+ * <li>{@link ContentHandler#endPrefixMapping(String)}</li>
+ * <li>{@link ContentHandler#endDocument()}</li>
+ * </ul>
+ * </p>
+ */
+public final class SaxWalkerOverDom {
+
+    private List<ContentHandler> listeners;
+
+    private static class Attr {
+
+        final QName qName;
+        final String qualifiedName;
+        final String value;
+
+        Attr(String namespace, String localName, String qualName, String val) {
+            qName = new QName(namespace, localName);
+            qualifiedName = qualName;
+            value = val;
+        }
+
+        Attr(Node node) {
+            this(node.getNamespaceURI(), node.getLocalName(), node.getNodeName(), node.getNodeValue());
+        }
+    }
+
+    private static class DomAttrsAsSax implements org.xml.sax.Attributes {
+
+        private final List<Attr> attributes;
+        private final Map<String, Attr> attrsByQualifiedName;
+        private final Map<QName, Attr> attrsByQName;
+        private final Map<String, Integer> indexByQualifiedName;
+        private final Map<QName, Integer> indexByQName;
+
+        DomAttrsAsSax(NamedNodeMap domAttrs) throws SAXException {
+            attributes = new ArrayList<Attr>();
+            attrsByQualifiedName = new HashMap<String, Attr>();
+            attrsByQName = new HashMap<QName, Attr>();
+
+            indexByQualifiedName = new HashMap<String, Integer>();
+            indexByQName = new HashMap<QName, Integer>();
+
+            if (domAttrs != null) {
+                for (int attrIdx = 0; attrIdx < domAttrs.getLength(); ++attrIdx) {
+                    final Node domAttr = domAttrs.item(attrIdx);
+
+                    if (Constants.XMLNS_ATTRIBUTE_NS_URI.equals(domAttr.getNamespaceURI())) {
+
+                        // Namespace declarations will be handled separately.
+                        continue;
+                    }
+
+                    final Attr attribute = new Attr(domAttr);
+                    attributes.add(attribute);
+
+                    attrsByQualifiedName.put(attribute.qualifiedName, attribute);
+                    attrsByQName.put(attribute.qName, attribute);
+
+                    indexByQualifiedName.put(attribute.qualifiedName, attrIdx);
+                    indexByQName.put(attribute.qName, attrIdx);
+                }
+            }
+        }
+
+        @Override
+        public int getLength() {
+            return attributes.size();
+        }
+
+        @Override
+        public String getURI(int index) {
+            if (attributes.size() <= index) {
+                return null;
+            } else {
+                return attributes.get(index).qName.getNamespaceURI();
+            }
+        }
+
+        @Override
+        public String getLocalName(int index) {
+            if (attributes.size() <= index) {
+                return null;
+            } else {
+                return attributes.get(index).qName.getLocalPart();
+            }
+        }
+
+        @Override
+        public String getQName(int index) {
+            if (attributes.size() <= index) {
+                return null;
+            } else {
+                return attributes.get(index).qualifiedName;
+            }
+        }
+
+        @Override
+        public String getType(int index) {
+            if (attributes.size() <= index) {
+                return null;
+            } else {
+                return "CDATA"; // We do not know the type information.
+            }
+        }
+
+        @Override
+        public String getValue(int index) {
+            if (attributes.size() <= index) {
+                return null;
+            } else {
+                return attributes.get(index).value;
+            }
+        }
+
+        @Override
+        public int getIndex(String uri, String localName) {
+            if ((uri == null) || (localName == null)) {
+                return -1;
+            }
+
+            final QName qName = new QName(uri, localName);
+            final Integer index = indexByQName.get(qName);
+
+            if (index == null) {
+                return -1;
+            } else {
+                return index;
+            }
+        }
+
+        @Override
+        public int getIndex(String qName) {
+            if (qName == null) {
+                return -1;
+            }
+
+            final Integer index = indexByQualifiedName.get(qName);
+            if (index == null) {
+                return -1;
+            } else {
+                return index;
+            }
+        }
+
+        @Override
+        public String getType(String uri, String localName) {
+            if ((uri == null) || (localName == null)) {
+                return null;
+            } else {
+                final Attr attr = attrsByQName.get(new QName(uri, localName));
+                return (attr == null) ? null : "CDATA";
+            }
+        }
+
+        @Override
+        public String getType(String qName) {
+            if (qName == null) {
+                return null;
+            } else {
+                final Attr attr = attrsByQualifiedName.get(qName);
+                return (attr == null) ? null : "CDATA";
+            }
+        }
+
+        @Override
+        public String getValue(String uri, String localName) {
+            if ((uri == null) || (localName == null)) {
+                return null;
+            } else {
+                final Attr attr = attrsByQName.get(new QName(uri, localName));
+                return (attr == null) ? null : attr.value;
+            }
+        }
+
+        @Override
+        public String getValue(String qName) {
+            if (qName == null) {
+                return null;
+            } else {
+                final Attr attr = attrsByQualifiedName.get(qName);
+                return (attr == null) ? null : attr.value;
+            }
+        }
+    }
+
+    /**
+     * Constructs a new <code>SaxWalkerOverDom</code>.
+     */
+    public SaxWalkerOverDom() {
+        listeners = null;
+    }
+
+    /**
+     * Constructs a new <code>SaxWalkerOverDom</code> with the provided
+     * {@link ContentHandler} to send SAX events.
+     *
+     * @param contentHandler The content handler to send events to.
+     */
+    public SaxWalkerOverDom(ContentHandler contentHandler) {
+        this();
+        listeners = new ArrayList<ContentHandler>(1);
+        listeners.add(contentHandler);
+    }
+
+    /**
+     * Constructs a new <code>SaxWalkerOverDom</code>, taking ownership of the
+     * list of {@link ContentHandler}s to send events to.
+     *
+     * @param contentHandlers The list of content handlers to send events to.
+     */
+    public SaxWalkerOverDom(List<ContentHandler> contentHandlers) {
+        this();
+        listeners = contentHandlers;
+    }
+
+    /**
+     * Adds the provided {@link ContentHandler} to the list of content handlers
+     * to send events to. If this content handler was already added, it will be
+     * sent events twice (or more often).
+     *
+     * @param contentHandler The content handler to send events to.
+     */
+    public void addContentHandler(ContentHandler contentHandler) {
+        if (listeners == null) {
+            listeners = new ArrayList<ContentHandler>(1);
+        }
+        listeners.add(contentHandler);
+    }
+
+    /**
+     * Removes the first instance of the provided {@link ContentHandler} from
+     * the set of handlers to send events to. If the content handler was added
+     * more than once, it will continue to receive events.
+     *
+     * @param contentHandler The content handler to stop sending events to.
+     * @return <code>true</code> if it was found, <code>false</code> if not.
+     */
+    public boolean removeContentHandler(ContentHandler contentHandler) {
+        if (listeners != null) {
+            return listeners.remove(contentHandler);
+        }
+        return false;
+    }
+
+    /**
+     * Walks the provided {@link Document}, sending events to all of the
+     * {@link ContentHandler}s as it traverses. If there are no content
+     * handlers, this method is a no-op.
+     *
+     * @param document The {@link Document} to traverse.
+     * @param systemId The system ID of this {@link Document}.
+     * @throws SAXException if an exception occurs when notifying the handlers.
+     */
+    public void walk(Document document) throws SAXException {
+        if (document == null) {
+            throw new IllegalArgumentException("Document cannot be null.");
+        }
+
+        if ((listeners == null) || listeners.isEmpty()) {
+            return;
+        }
+
+        for (ContentHandler listener : listeners) {
+            listener.startDocument();
+        }
+
+        final List<String> prefixes = startPrefixMappings(document);
+
+        walk(document.getDocumentElement());
+
+        for (ContentHandler listener : listeners) {
+            for (String prefix : prefixes) {
+                listener.endPrefixMapping(prefix);
+            }
+            listener.endDocument();
+        }
+    }
+
+    private void walk(Element element) throws SAXException {
+        DomAttrsAsSax attrs = new DomAttrsAsSax(element.getAttributes());
+
+        final List<String> prefixes = startPrefixMappings(element);
+
+        for (ContentHandler listener : listeners) {
+            listener.startElement(convertNullToEmptyString(element.getNamespaceURI()),
+                                  convertNullToEmptyString(element.getLocalName()),
+                                  convertNullToEmptyString(element.getNodeName()), attrs);
+        }
+
+        NodeList children = element.getChildNodes();
+
+        for (int childIndex = 0; childIndex < children.getLength(); ++childIndex) {
+            Node node = children.item(childIndex);
+            if (node instanceof Element) {
+                walk((Element)node);
+            } else if (node instanceof Text) {
+                walk((Text)node);
+            } else if (node instanceof org.w3c.dom.Comment) {
+                // Ignored.
+            } else {
+                throw new SAXException("Unrecognized child of " + element.getTagName() + " of type "
+                                       + node.getClass().getName());
+            }
+        }
+
+        for (ContentHandler listener : listeners) {
+            listener.endElement(convertNullToEmptyString(element.getNamespaceURI()),
+                                convertNullToEmptyString(element.getLocalName()),
+                                convertNullToEmptyString(element.getNodeName()));
+
+            for (String prefix : prefixes) {
+                listener.endPrefixMapping(prefix);
+            }
+        }
+    }
+
+    private void walk(Text text) throws SAXException {
+        /*
+         * TODO: getData() may throw a org.w3c.dom.DOMException if the actual
+         * text data is too large to fit into a single DOMString (the DOM impl's
+         * internal storage of text data). If that's the case, substringData()
+         * must be called to retrieve the data in pieces. The documentation does
+         * not supply information on the maximum DOMString size; it appears to
+         * require trial & error.
+         */
+        if (text.getLength() > 0) {
+            char[] data = text.getData().toCharArray();
+            for (ContentHandler listener : listeners) {
+                listener.characters(data, 0, data.length);
+            }
+        }
+    }
+
+    private static String convertNullToEmptyString(String input) {
+        if (input == null) {
+            return "";
+        }
+        return input;
+    }
+
+    private List<String> startPrefixMappings(Node node) throws DOMException, SAXException {
+
+        switch (node.getNodeType()) {
+        case Node.DOCUMENT_NODE:
+        case Node.ELEMENT_NODE:
+            break;
+        default:
+            throw new IllegalArgumentException("Cannot start prefix mappings for a node of type "
+                                               + node.getNodeType());
+        }
+
+        final ArrayList<String> prefixes = new ArrayList<String>();
+
+        final NamedNodeMap attrs = node.getAttributes();
+        if (attrs == null) {
+            return prefixes;
+        }
+
+        for (int attrIndex = 0; attrIndex < attrs.getLength(); ++attrIndex) {
+            final Node attr = attrs.item(attrIndex);
+            final String attrUri = attr.getNamespaceURI();
+
+            if (Constants.XMLNS_ATTRIBUTE_NS_URI.equals(attrUri)) {
+                final String localName = attr.getLocalName();
+                String prefix = null;
+
+                if (Constants.XMLNS_ATTRIBUTE.equals(localName)) {
+                    prefix = Constants.DEFAULT_NS_PREFIX;
+                } else {
+                    prefix = localName;
+                }
+
+                prefixes.add(prefix);
+
+                for (ContentHandler listener : listeners) {
+                    listener.startPrefixMapping(prefix, attr.getNodeValue());
+                }
+            }
+        }
+
+        return prefixes;
+    }
+}

Propchange: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/SaxWalkerOverDom.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaDocumentNode.java
URL: http://svn.apache.org/viewvc/webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaDocumentNode.java?rev=1625632&view=auto
==============================================================================
--- webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaDocumentNode.java (added)
+++ webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaDocumentNode.java Wed Sep 17 15:32:44 2014
@@ -0,0 +1,263 @@
+/**
+ * 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.docpath;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * The <code>XmlSchemaDocumentNode</code> represents a node in the XML Schema as
+ * it is used by an XML document. As {@link XmlSchemaPathFinder} walks through
+ * an XML document, it builds {@link XmlSchemaPathNode}s representing the path
+ * walked, and <code>XmlSchemaDocumentNode</code>s representing where the XML
+ * document's elements fall in the XML Schema's sequences, choices, and all
+ * groups.
+ * <p>
+ * While {@link XmlSchemaStateMachineNode}s may loop back on themselves, the
+ * <code>XmlSchemaDocumentNode</code>s will not. Likewise, the document nodes
+ * form a tree that represent the XML Schema as it is applied to the document.
+ * </p>
+ * <p>
+ * If a single node has multiple occurrences, the children of each occurrence
+ * can be retrieved by calling {@link #getChildren(int)}. {@link #getChildren()}
+ * returns the child nodes for the final occurrence.
+ * </p>
+ */
+public final class XmlSchemaDocumentNode<U> {
+
+    private XmlSchemaStateMachineNode stateMachineNode;
+    private XmlSchemaDocumentNode<U> parent;
+    private List<SortedMap<Integer, XmlSchemaDocumentNode<U>>> children;
+    private List<XmlSchemaPathNode> visitors;
+    private boolean receivedContent;
+    private U userDefinedContent;
+
+    XmlSchemaDocumentNode(XmlSchemaDocumentNode<U> parent, XmlSchemaStateMachineNode stateMachineNode) {
+
+        userDefinedContent = null;
+        set(parent, stateMachineNode);
+    }
+
+    /**
+     * Returns the {@link XmlSchemaStateMachineNode} representing the place in
+     * the XML Schema that this <code>XmlSchemaDocumentNode</code> represents.
+     */
+    public XmlSchemaStateMachineNode getStateMachineNode() {
+        return stateMachineNode;
+    }
+
+    /**
+     * The <code>XmlSchemaDocumentNode</code> representing this one's immediate
+     * parent.
+     */
+    public XmlSchemaDocumentNode<U> getParent() {
+        return parent;
+    }
+
+    /**
+     * Retrieves the children in the last occurrence of this node, mapped to
+     * their relative position.
+     */
+    public SortedMap<Integer, XmlSchemaDocumentNode<U>> getChildren() {
+        if (children == null) {
+            return null;
+        } else {
+            return getChildren(children.size());
+        }
+    }
+
+    /**
+     * Retrieves the children in the provided occurrence of this node, mapped to
+     * their relative position.
+     *
+     * @param iteration The 1-based occurrence to retrieve children for.
+     */
+    public SortedMap<Integer, XmlSchemaDocumentNode<U>> getChildren(int iteration) {
+        if ((children == null) || (children.size() < iteration) || (iteration < 1)) {
+            return null;
+        } else {
+            return children.get(iteration - 1);
+        }
+    }
+
+    /**
+     * Indicates whether an element has text in it.
+     */
+    boolean getReceivedContent() {
+        return receivedContent;
+    }
+
+    /**
+     * Sets whether the element has text in it.
+     * 
+     * @param receivedContent
+     */
+    void setReceivedContent(boolean receivedContent) {
+        this.receivedContent = receivedContent;
+    }
+
+    /**
+     * A visitor is a CHILD or SIBLING {@link XmlSchemaPathNode} entering this
+     * <code>XmlSchemaDocumentNode</code>. This is used to keep track of how
+     * many occurrences are active via the current path winding through the
+     * schema.
+     */
+    void addVisitor(XmlSchemaPathNode path) {
+        if (path.getDocumentNode() != this) {
+            throw new IllegalArgumentException("Path node must have this XmlSchemaDocumentNode "
+                                               + "as its document node.");
+        }
+
+        switch (path.getDirection()) {
+        case CHILD:
+        case SIBLING:
+            break;
+        default:
+            throw new IllegalArgumentException("Only CHILD and SIBLING paths may be visitors of an "
+                                               + "XmlSchemaDocumentNode, not a " + path.getDirection()
+                                               + " path.");
+        }
+
+        if (visitors == null) {
+            visitors = new ArrayList<XmlSchemaPathNode>(4);
+        }
+
+        if (children != null) {
+            if (children.size() == visitors.size()) {
+                children.add(new TreeMap<Integer, XmlSchemaDocumentNode<U>>());
+            } else {
+                throw new IllegalStateException(
+                                                "Attempted to add a new visitor when the number of occurrences ("
+                                                    + children.size()
+                                                    + ") did not match the number of existing visitors ("
+                                                    + visitors.size() + ").");
+            }
+        }
+
+        visitors.add(path);
+    }
+
+    boolean removeVisitor(XmlSchemaPathNode path) {
+        if ((visitors == null) || visitors.isEmpty()) {
+            return false;
+        }
+
+        if ((children != null) && (visitors.size() != children.size())) {
+            throw new IllegalStateException("The number of visitors (" + visitors.size()
+                                            + ") does not match the number of occurrences ("
+                                            + children.size() + ").");
+        }
+
+        int visitorIndex = 0;
+        for (; visitorIndex < visitors.size(); ++visitorIndex) {
+            if (visitors.get(visitorIndex) == path) {
+                break;
+            }
+        }
+
+        if (visitors.size() == visitorIndex) {
+            return false;
+        }
+
+        visitors.remove(visitorIndex);
+
+        if (children != null) {
+            children.remove(visitorIndex);
+        }
+
+        return true;
+    }
+
+    /**
+     * The total number of occurrences of this
+     * <code>XmlSchemaDocumentNode</code> in the underlying document.
+     */
+    public int getIteration() {
+        if ((children != null) && (children.size() != visitors.size())) {
+            throw new IllegalStateException("The number of occurrences (" + children.size()
+                                            + ") is not equal to the number of visitors (" + visitors.size()
+                                            + ").");
+        }
+        return visitors.size();
+    }
+
+    /**
+     * Shortcut for calling <code>getStateMachineNode().getMinOccurs()</code>.
+     *
+     * @see XmlSchemaStateMachineNode#getMinOccurs()
+     */
+    public long getMinOccurs() {
+        return stateMachineNode.getMinOccurs();
+    }
+
+    /**
+     * Shortcut for calling <code>getStateMachineNode().getMaxOccurs()</code>.
+     *
+     * @see XmlSchemaStateMachineNode#getMaxOccurs()
+     */
+    public long getMaxOccurs() {
+        return stateMachineNode.getMaxOccurs();
+    }
+
+    int getSequencePosition() {
+        if ((children == null)
+            || (!stateMachineNode.getNodeType().equals(XmlSchemaStateMachineNode.Type.SEQUENCE))) {
+            return -1;
+        } else if (children.isEmpty()) {
+            return 0;
+        } else if (children.get(children.size() - 1).isEmpty()) {
+            return 0;
+        } else {
+            return children.get(children.size() - 1).lastKey();
+        }
+    }
+
+    void set(XmlSchemaDocumentNode parent, XmlSchemaStateMachineNode stateMachineNode) {
+
+        this.parent = parent;
+        this.stateMachineNode = stateMachineNode;
+        this.receivedContent = false;
+        this.visitors = null;
+
+        if ((this.stateMachineNode.getPossibleNextStates() == null)
+            || this.stateMachineNode.getPossibleNextStates().isEmpty()) {
+            this.children = null;
+
+        } else {
+            this.children = new ArrayList<SortedMap<Integer, XmlSchemaDocumentNode<U>>>(1);
+        }
+    }
+
+    /**
+     * Retrieves any user-defined content attached to this
+     * <code>XmlSchemaDocumentNode</code>, or <code>null</code> if none.
+     */
+    public U getUserDefinedContent() {
+        return userDefinedContent;
+    }
+
+    /**
+     * Attaches user-defined content to this <code>XmlSchemaDocumentNode</code>.
+     */
+    public void setUserDefinedContent(U userDefinedContent) {
+        this.userDefinedContent = userDefinedContent;
+    }
+}

Propchange: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaDocumentNode.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java
URL: http://svn.apache.org/viewvc/webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java?rev=1625632&view=auto
==============================================================================
--- webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java (added)
+++ webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java Wed Sep 17 15:32:44 2014
@@ -0,0 +1,778 @@
+/**
+ * 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.docpath;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.xml.bind.DatatypeConverter;
+import javax.xml.bind.ValidationException;
+import javax.xml.datatype.DatatypeConfigurationException;
+import javax.xml.datatype.DatatypeFactory;
+import javax.xml.namespace.NamespaceContext;
+import javax.xml.namespace.QName;
+
+import org.apache.ws.commons.schema.XmlSchemaAttribute;
+import org.apache.ws.commons.schema.XmlSchemaElement;
+import org.apache.ws.commons.schema.XmlSchemaUse;
+import org.apache.ws.commons.schema.walker.XmlSchemaAttrInfo;
+import org.apache.ws.commons.schema.walker.XmlSchemaRestriction;
+import org.apache.ws.commons.schema.walker.XmlSchemaTypeInfo;
+import org.xml.sax.Attributes;
+
+/**
+ * Methods to confirm that an XML element and its attributes all conform to its
+ * XML Schema.
+ */
+final class XmlSchemaElementValidator {
+
+    private static DatatypeFactory datatypeFactory = null;
+
+    private static DatatypeFactory getDatatypeFactory() {
+        if (datatypeFactory == null) {
+            try {
+                datatypeFactory = DatatypeFactory.newInstance();
+            } catch (DatatypeConfigurationException e) {
+                throw new IllegalStateException(
+                                                "Unable to create the DatatypeFactory for validating XML Schema "
+                                                    + "durations.", e);
+            }
+        }
+        return datatypeFactory;
+    }
+
+    /**
+     * Confirms all of the SAX {@link Attributes} provided conform to their
+     * types listed in the {@link XmlSchemaStateMachineNode}. If one of the
+     * types is a {@link QName}, uses the {@link NamespaceContext} to confirm
+     * its namespace is recognized.
+     * <p>
+     * Throws a {@link ValidationException} if the content is not valid.
+     * </p>
+     *
+     * @param state The state machine node containing the attribute types.
+     * @param attrs The attributes to verify.
+     * @param nsContext The namespace context to use to confirm QNames of.
+     * @throws ValidationException If the content is not valid.
+     */
+    static void validateAttributes(XmlSchemaStateMachineNode state, Attributes attrs,
+                                   NamespaceContext nsContext) throws ValidationException {
+
+        if ((state == null) || (attrs == null) || (nsContext == null)
+            || !state.getNodeType().equals(XmlSchemaStateMachineNode.Type.ELEMENT)) {
+            throw new ValidationException("None of state, attrs, or nsContext can be null, and state must"
+                                          + " be of an SchemaStateMachineNode.Type.ELEMENT node, not "
+                                          + ((state == null) ? null : state.getNodeType()));
+        }
+
+        final List<XmlSchemaAttrInfo> attributes = state.getAttributes();
+
+        if ((attributes == null) || attributes.isEmpty()) {
+            // Nothing to validate.
+            return;
+        }
+
+        final QName elemQName = state.getElement().getQName();
+
+        for (XmlSchemaAttrInfo attribute : attributes) {
+            final XmlSchemaAttribute xmlSchemaAttr = attribute.getAttribute();
+            final QName attrQName = xmlSchemaAttr.getQName();
+            final XmlSchemaUse use = xmlSchemaAttr.getUse();
+
+            String value = attrs.getValue(attrQName.getNamespaceURI(), attrQName.getLocalPart());
+
+            if (value == null) {
+                // A namespace is not always available.
+                value = attrs.getValue("", attrQName.getLocalPart());
+            }
+
+            if (value != null) {
+                value = value.trim();
+            }
+
+            // Confirm the attribute is used correctly.
+            switch (use) {
+            case OPTIONAL:
+                break;
+            case PROHIBITED:
+                if ((value != null) && (value.length() > 0)) {
+                    throw new ValidationException("Attribute " + attrQName + " was declared 'prohibited' by "
+                                                  + elemQName + " and cannot have a value.");
+                }
+                break;
+            case REQUIRED:
+                if ((value == null) || (value.length() == 0)) {
+                    throw new ValidationException("Attribute " + attrQName + " was declared 'required' by "
+                                                  + elemQName + " and must have a value.");
+                }
+                break;
+            case NONE:
+                /*
+                 * An attribute with no usage is optional, which was already
+                 * taken care of by XmlSchemaWalker.
+                 */
+            default:
+                throw new ValidationException("Attribute " + attrQName + " of element " + elemQName
+                                              + " has an unrecognized usage of " + use + ".");
+            }
+
+            /*
+             * If the value is null or empty there is no further validation we
+             * can perform here.
+             */
+            if ((value == null) || (value.length() == 0)) {
+                continue;
+            }
+
+            if (attribute.getType().getType().equals(XmlSchemaTypeInfo.Type.COMPLEX)) {
+
+                throw new ValidationException("Attribute " + attrQName + " of element " + elemQName
+                                              + " cannot have a COMPLEX type.");
+            }
+
+            validateType("Attribute " + attrQName + " of " + elemQName, value, attribute.getType(), nsContext);
+        }
+    }
+
+    /**
+     * Confirms the provided content conforms to the element's expected content
+     * type. If the expected content is a {@link QName}, uses the provided
+     * {@link NamespaceContext} to confirm the namespace is recognized and
+     * valid.
+     * <p>
+     * Throws a {@link ValidationException} if the element's content is invalid.
+     * </p>
+     *
+     * @param state The {@link XmlSchemaStateMachineNode} containing the type
+     *            information of the element's expected content.
+     * @param elementContent The element content to verify.
+     * @param nsContext The <code>NamespaceContext</code> to use to verify
+     *            <code>QName</code>s are valid.
+     * @throws ValidationException if the element content is not valid.
+     */
+    static void validateContent(XmlSchemaStateMachineNode state, String elementContent,
+                                NamespaceContext nsContext) throws ValidationException {
+
+        if ((state == null) || (nsContext == null)
+            || !state.getNodeType().equals(XmlSchemaStateMachineNode.Type.ELEMENT)) {
+            throw new ValidationException("Niether state nor nsContext can be null, and state must be of an "
+                                          + "SchemaStateMachineNode.Type.ELEMENT node, not "
+                                          + ((state == null) ? null : state.getNodeType()));
+        }
+
+        final QName elemQName = state.getElement().getQName();
+        final XmlSchemaTypeInfo elemType = state.getElementType();
+        final XmlSchemaElement element = state.getElement();
+
+        if (elementContent != null) {
+            elementContent = elementContent.trim();
+        }
+
+        switch (elemType.getType()) {
+        case COMPLEX: {
+            if (!elemType.isMixed() && (elementContent != null) && (elementContent.length() > 0)) {
+
+                /*
+                 * If a type is COMPLEX, then it either is a mixed type or it
+                 * only has elements as children. Likewise, if the text is not
+                 * null or empty, and the type is not mixed, then element
+                 * content is where it is not expected.
+                 */
+                throw new ValidationException(elemQName
+                                              + " is a non-mixed complex type, therefore there should"
+                                              + " not be any content between the tags, like \""
+                                              + elementContent + "\".");
+            }
+            break;
+        }
+        case ATOMIC:
+        case LIST:
+        case UNION: {
+            if ((elementContent == null) || (elementContent.length() == 0)) {
+                if (state.getElement().isNillable()) {
+                    // Null is a perfectly valid state.
+                    return;
+                } else {
+                    elementContent = element.getDefaultValue();
+                    if (elementContent == null) {
+                        elementContent = element.getFixedValue();
+                    }
+                    if (elementContent == null) {
+                        throw new ValidationException(
+                                                      "Element "
+                                                          + elemQName
+                                                          + " has no content, no default value, and no fixed value,"
+                                                          + " but is of type " + elemType.getType() + ".");
+                    }
+                }
+            }
+            validateType(elemQName.toString(), elementContent, elemType, nsContext);
+            break;
+        }
+        default:
+            throw new IllegalStateException(elemQName + " has an unrecognized content type of "
+                                            + elemType.getType() + ".");
+        }
+    }
+
+    private static void validateType(String name, String value, XmlSchemaTypeInfo typeInfo,
+                                     NamespaceContext nsContext) throws ValidationException {
+
+        if ((value == null) || (value.length() == 0)) {
+            throw new ValidationException(name + " cannot have a null or empty value!");
+        }
+
+        final HashMap<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets = typeInfo.getFacets();
+
+        switch (typeInfo.getType()) {
+        case ATOMIC:
+            validateAtomicType(name, value, typeInfo, nsContext);
+            break;
+        case LIST: {
+            /*
+             * A list is a whitespace-separated series of elements. Split the
+             * list and perform a type-check on the items.
+             */
+            final String[] values = value.split(" ");
+            for (String item : values) {
+                validateType(name + " item value \"" + item + "\"", item, typeInfo.getChildTypes().get(0),
+                             nsContext);
+            }
+            listLengthChecks(name, values, facets);
+            break;
+        }
+        case UNION: {
+            /*
+             * We just want to confirm that the value we are given validates
+             * against at least one of the types; we do not care which one.
+             */
+            boolean foundValidType = false;
+            for (XmlSchemaTypeInfo unionType : typeInfo.getChildTypes()) {
+                try {
+                    validateType(name, value, unionType, nsContext);
+                    foundValidType = true;
+                    break;
+                } catch (ValidationException e) {
+                    // The type did not validate; try another.
+                }
+            }
+            if (!foundValidType) {
+                StringBuilder errMsg = new StringBuilder(name);
+                errMsg.append(" does not validate against any of its union of");
+                errMsg.append(" types.  The value is \"").append(value);
+                errMsg.append("\" and the union types are: ");
+
+                for (int childIndex = 0; childIndex < typeInfo.getChildTypes().size() - 1; ++childIndex) {
+
+                    errMsg.append(typeInfo.getChildTypes().get(childIndex).getBaseType());
+                    errMsg.append(", ");
+
+                }
+                errMsg
+                    .append(typeInfo.getChildTypes().get(typeInfo.getChildTypes().size() - 1).getBaseType());
+                errMsg.append('.');
+
+                throw new ValidationException(errMsg.toString());
+            }
+            break;
+        }
+        case COMPLEX:
+            // This only validates if the type is mixed.
+            if (!typeInfo.isMixed()) {
+                throw new ValidationException(name + " has a value of \"" + value
+                                              + "\" but it represents a non-mixed complex type.");
+            }
+            break;
+        default:
+            throw new ValidationException(name + " has an unrecognized type of " + typeInfo.getType());
+        }
+    }
+
+    private static void validateAtomicType(String name, String value, XmlSchemaTypeInfo typeInfo,
+                                           NamespaceContext nsContext) throws ValidationException {
+
+        if (!typeInfo.getType().equals(XmlSchemaTypeInfo.Type.ATOMIC)) {
+            throw new ValidationException(name + " must have a type of ATOMIC, not " + typeInfo.getType());
+
+        } else if ((value == null) || (value.length() == 0)) {
+            throw new ValidationException(name + " cannot have a null or empty value when validating.");
+        }
+
+        final Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets = typeInfo.getFacets();
+
+        switch (typeInfo.getBaseType()) {
+        case ANYTYPE:
+        case ANYSIMPLETYPE:
+        case ANYURI:
+            /*
+             * anyURI has no equivalent type in Java. (from
+             * http://docs.oracle.com/cd/E19159-01/819-3669/bnazf/index.html)
+             */
+        case STRING:
+            // Text plus facets.
+            stringLengthChecks(name, DatatypeConverter.parseString(value), facets);
+            break;
+
+        case DURATION:
+            try {
+                getDatatypeFactory().newDuration(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid duration.",
+                                              iae);
+            }
+            break;
+
+        case DATETIME:
+            try {
+                DatatypeConverter.parseDateTime(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid date-time.",
+                                              iae);
+            }
+            break;
+
+        case TIME:
+            try {
+                DatatypeConverter.parseTime(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid time.", iae);
+            }
+            break;
+
+        case DATE:
+            try {
+                DatatypeConverter.parseDate(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid date.", iae);
+            }
+            break;
+
+        case YEARMONTH:
+            try {
+                getDatatypeFactory().newXMLGregorianCalendar(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(
+                                              name + " value of \"" + value + "\" is not a valid Year-Month.",
+                                              iae);
+            }
+            break;
+
+        case YEAR:
+            try {
+                getDatatypeFactory().newXMLGregorianCalendar(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid year.", iae);
+            }
+            break;
+
+        case MONTHDAY:
+            try {
+                getDatatypeFactory().newXMLGregorianCalendar(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid month-day.",
+                                              iae);
+            }
+            break;
+
+        case DAY:
+            try {
+                getDatatypeFactory().newXMLGregorianCalendar(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid day.", iae);
+            }
+            break;
+
+        case MONTH:
+            try {
+                getDatatypeFactory().newXMLGregorianCalendar(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid month.", iae);
+            }
+            break;
+
+        // Dates
+        case BOOLEAN:
+            if (!value.equalsIgnoreCase("true") && !value.equalsIgnoreCase("false")) {
+                throw new ValidationException(name + " value of \"" + value
+                                              + "\" is not a valid boolean; must be \"true\" or \"false\".");
+            }
+            break;
+
+        case BIN_BASE64:
+            try {
+                DatatypeConverter.parseBase64Binary(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value
+                                              + "\" is not valid base-64 binary.", iae);
+            }
+            break;
+
+        case BIN_HEX:
+            try {
+                DatatypeConverter.parseHexBinary(value);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value
+                                              + "\" is not valid hexadecimal binary.", iae);
+            }
+            break;
+
+        case FLOAT:
+            try {
+                rangeChecks(name, new BigDecimal(DatatypeConverter.parseFloat(value)), facets);
+            } catch (NumberFormatException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid float.", iae);
+            }
+            break;
+
+        case DECIMAL:
+            try {
+                final BigDecimal decimal = DatatypeConverter.parseDecimal(value);
+                rangeChecks(name, decimal, facets);
+                digitsFacetChecks(name, decimal, facets);
+            } catch (NumberFormatException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid decimal.",
+                                              iae);
+            }
+            break;
+
+        case DOUBLE:
+            try {
+                rangeChecks(name, new BigDecimal(DatatypeConverter.parseDouble(value)), facets);
+            } catch (NumberFormatException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid double.",
+                                              iae);
+            }
+            break;
+
+        case QNAME:
+            try {
+                DatatypeConverter.parseQName(value, nsContext);
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value + "\" is not a valid .", iae);
+            }
+            break;
+
+        case NOTATION:
+            try {
+                /*
+                 * The value space of NOTATION is the set of QNames of notations
+                 * declared in the current schema.
+                 */
+                final String[] qNames = value.split(" ");
+                for (String qName : qNames) {
+                    DatatypeConverter.parseQName(qName, nsContext);
+                }
+
+            } catch (IllegalArgumentException iae) {
+                throw new ValidationException(name + " value of \"" + value
+                                              + "\" is not a valid series of QNames.", iae);
+            }
+            break;
+
+        default:
+            throw new ValidationException(name + " has an unrecognized base value type of "
+                                          + typeInfo.getBaseType());
+        }
+
+        checkEnumerationFacet(name, value, facets);
+    }
+
+    private static void rangeChecks(String name, BigDecimal value,
+                                    Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets)
+        throws ValidationException {
+
+        if (facets == null) {
+            return;
+        }
+
+        rangeCheck(name, value, facets, XmlSchemaRestriction.Type.EXCLUSIVE_MIN);
+        rangeCheck(name, value, facets, XmlSchemaRestriction.Type.INCLUSIVE_MIN);
+        rangeCheck(name, value, facets, XmlSchemaRestriction.Type.EXCLUSIVE_MAX);
+        rangeCheck(name, value, facets, XmlSchemaRestriction.Type.INCLUSIVE_MAX);
+    }
+
+    private static void rangeCheck(String name, BigDecimal value,
+                                   Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets,
+                                   XmlSchemaRestriction.Type rangeType) throws ValidationException {
+
+        final List<XmlSchemaRestriction> rangeFacets = facets.get(rangeType);
+
+        boolean satisfied = true;
+        BigDecimal compareTo = null;
+
+        if ((rangeFacets != null) && !rangeFacets.isEmpty()) {
+            for (XmlSchemaRestriction rangeFacet : rangeFacets) {
+                compareTo = getBigDecimalOf(rangeFacet.getValue());
+                final int comparison = value.compareTo(compareTo);
+
+                switch (rangeType) {
+                case EXCLUSIVE_MIN:
+                    satisfied = (comparison > 0);
+                    break;
+                case INCLUSIVE_MIN:
+                    satisfied = (comparison >= 0);
+                    break;
+                case EXCLUSIVE_MAX:
+                    satisfied = (comparison < 0);
+                    break;
+                case INCLUSIVE_MAX:
+                    satisfied = (comparison <= 0);
+                    break;
+                default:
+                    throw new ValidationException("Cannot perform a range check of type " + rangeType);
+                }
+
+                if (!satisfied) {
+                    break;
+                }
+            }
+        }
+
+        if (!satisfied) {
+            throw new ValidationException(name + " value \"" + value + "\" violates the " + rangeType
+                                          + " restriction of " + compareTo + ".");
+        }
+    }
+
+    private static BigDecimal getBigDecimalOf(Object numericValue) {
+        BigDecimal newValue = null;
+
+        if (numericValue instanceof BigDecimal) {
+            newValue = (BigDecimal)numericValue;
+
+        } else if (numericValue instanceof Double) {
+            newValue = new BigDecimal(((Double)numericValue).doubleValue());
+
+        } else if (numericValue instanceof Float) {
+            newValue = new BigDecimal(((Float)numericValue).floatValue());
+
+        } else if (numericValue instanceof BigInteger) {
+            newValue = new BigDecimal((BigInteger)numericValue);
+
+        } else if (numericValue instanceof Number) {
+            newValue = new BigDecimal(((Number)numericValue).longValue());
+
+        } else if (numericValue instanceof String) {
+            newValue = new BigDecimal(numericValue.toString());
+
+        } else {
+            throw new IllegalArgumentException(numericValue.getClass().getName()
+                                               + " is not a subclass of java.lang.Number.");
+        }
+
+        return newValue;
+    }
+
+    private static void stringLengthChecks(String name, String value,
+                                           Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets)
+        throws ValidationException {
+
+        if (facets == null) {
+            return;
+        }
+
+        stringLengthCheck(name, value, facets, XmlSchemaRestriction.Type.LENGTH);
+
+        stringLengthCheck(name, value, facets, XmlSchemaRestriction.Type.LENGTH_MIN);
+
+        stringLengthCheck(name, value, facets, XmlSchemaRestriction.Type.LENGTH_MAX);
+    }
+
+    private static void stringLengthCheck(String name, String value,
+                                          Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets,
+                                          XmlSchemaRestriction.Type facetType) throws ValidationException {
+
+        final List<XmlSchemaRestriction> lengthFacets = facets.get(facetType);
+        int lengthRestriction = -1;
+        boolean satisfied = true;
+
+        if (lengthFacets != null) {
+            for (XmlSchemaRestriction lengthFacet : lengthFacets) {
+                lengthRestriction = Integer.parseInt(lengthFacet.getValue().toString());
+
+                switch (facetType) {
+                case LENGTH:
+                    satisfied = (value.length() == lengthRestriction);
+                    break;
+                case LENGTH_MIN:
+                    satisfied = (value.length() >= lengthRestriction);
+                    break;
+                case LENGTH_MAX:
+                    satisfied = (value.length() <= lengthRestriction);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Cannot perform a length restriction of type "
+                                                       + facetType);
+                }
+
+                if (!satisfied) {
+                    break;
+                }
+            }
+        }
+
+        if (!satisfied) {
+            throw new ValidationException(name + " value \"" + value + "\" does not meet the " + facetType
+                                          + " restriction of " + lengthRestriction + ".");
+        }
+    }
+
+    private static void listLengthChecks(String name, String[] value,
+                                         Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets)
+        throws ValidationException {
+
+        if (facets == null) {
+            return;
+        }
+
+        listLengthCheck(name, value, facets, XmlSchemaRestriction.Type.LENGTH);
+        listLengthCheck(name, value, facets, XmlSchemaRestriction.Type.LENGTH_MIN);
+        listLengthCheck(name, value, facets, XmlSchemaRestriction.Type.LENGTH_MAX);
+    }
+
+    private static void listLengthCheck(String name, String[] value,
+                                        Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets,
+                                        XmlSchemaRestriction.Type facetType) throws ValidationException {
+
+        final List<XmlSchemaRestriction> lengthFacets = facets.get(facetType);
+        int lengthRestriction = -1;
+        boolean satisfied = true;
+
+        if (lengthFacets != null) {
+            for (XmlSchemaRestriction lengthFacet : lengthFacets) {
+                lengthRestriction = Integer.parseInt(lengthFacet.getValue().toString());
+
+                switch (facetType) {
+                case LENGTH:
+                    satisfied = (value.length == lengthRestriction);
+                    break;
+                case LENGTH_MIN:
+                    satisfied = (value.length >= lengthRestriction);
+                    break;
+                case LENGTH_MAX:
+                    satisfied = (value.length <= lengthRestriction);
+                    break;
+                default:
+                    throw new IllegalArgumentException("Cannot perform a length restriction of type "
+                                                       + facetType);
+                }
+
+                if (!satisfied) {
+                    break;
+                }
+            }
+        }
+
+        if (!satisfied) {
+            throw new ValidationException(name + " value of length " + value.length + " does not meet the "
+                                          + facetType + " restriction of " + lengthRestriction + ".");
+        }
+    }
+
+    private static void digitsFacetChecks(String name, BigDecimal value,
+                                          Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets)
+        throws ValidationException {
+
+        if (facets == null) {
+            return;
+        }
+
+        digitsFacetCheck(name, value, facets, XmlSchemaRestriction.Type.DIGITS_FRACTION);
+
+        digitsFacetCheck(name, value, facets, XmlSchemaRestriction.Type.DIGITS_TOTAL);
+    }
+
+    private static void digitsFacetCheck(String name, BigDecimal value,
+                                         Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets,
+                                         XmlSchemaRestriction.Type facetType) throws ValidationException {
+
+        final List<XmlSchemaRestriction> digitsFacets = facets.get(facetType);
+        int numDigits = 0;
+        boolean satisfied = true;
+
+        if (digitsFacets != null) {
+            for (XmlSchemaRestriction digitsFacet : digitsFacets) {
+                numDigits = Integer.parseInt(digitsFacet.getValue().toString());
+                switch (facetType) {
+                case DIGITS_FRACTION:
+                    satisfied = (value.scale() <= numDigits);
+                    break;
+                case DIGITS_TOTAL: {
+                    satisfied = (value.precision() <= numDigits);
+                    break;
+                }
+                default:
+                    throw new IllegalArgumentException(
+                                                       "Cannot perform a digits facet check with a facet of type "
+                                                           + facetType);
+                }
+            }
+        }
+
+        if (!satisfied) {
+            StringBuilder errMsg = new StringBuilder(name);
+            errMsg.append(" value \"").append(value);
+            errMsg.append("\" does not meet the ").append(facetType);
+            errMsg.append(" check of ").append(numDigits).append(" digits.");
+
+            throw new ValidationException(errMsg.toString());
+        }
+    }
+
+    private static void checkEnumerationFacet(String name,
+                                              String value,
+                                              Map<XmlSchemaRestriction.Type, List<XmlSchemaRestriction>> facets)
+        throws ValidationException {
+
+        if (facets == null) {
+            return;
+        }
+
+        final List<XmlSchemaRestriction> enumFacets = facets.get(XmlSchemaRestriction.Type.ENUMERATION);
+
+        if (enumFacets == null) {
+            return;
+        }
+
+        boolean found = false;
+        for (XmlSchemaRestriction enumFacet : enumFacets) {
+            if (value.equals(enumFacet.getValue().toString())) {
+                found = true;
+                break;
+            }
+        }
+
+        if (!found) {
+            StringBuilder errMsg = new StringBuilder(name);
+            errMsg.append(" value \"").append(value).append("\" is not a member of");
+            errMsg.append(" the enumeration {\"");
+            for (int enumIndex = 0; enumIndex < enumFacets.size() - 1; ++enumIndex) {
+                errMsg.append(enumFacets.get(enumIndex).getValue()).append("\", \"");
+            }
+            errMsg.append(enumFacets.get(enumFacets.size() - 1).getValue());
+            errMsg.append("\"}.");
+
+            throw new ValidationException(errMsg.toString());
+        }
+    }
+}

Propchange: webservices/xmlschema/trunk/xmlschema-walker/src/main/java/org/apache/ws/commons/schema/docpath/XmlSchemaElementValidator.java
------------------------------------------------------------------------------
    svn:eol-style = native