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