[Practicalxml-commits] SF.net SVN: practicalxml:[62] trunk/src
Brought to you by:
kdgregory
|
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-28 15:56:13
|
Revision: 62
http://practicalxml.svn.sourceforge.net/practicalxml/?rev=62&view=rev
Author: kdgregory
Date: 2008-12-28 15:56:10 +0000 (Sun, 28 Dec 2008)
Log Message:
-----------
Bug 2417477: SchemaUtil.newSchema() is for simple case, combineSchema() for bug workaround and ordering
Modified Paths:
--------------
trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java
trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java
Modified: trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java
===================================================================
--- trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2008-12-28 00:46:35 UTC (rev 61)
+++ trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2008-12-28 15:56:10 UTC (rev 62)
@@ -1,5 +1,9 @@
package net.sf.practicalxml;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeSet;
import javax.xml.XMLConstants;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
@@ -11,7 +15,7 @@
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
-import org.apache.commons.lang.StringUtils;
+import net.sf.practicalxml.util.ExceptionErrorHandler;
/**
@@ -19,54 +23,15 @@
* documents. Since <code>javax.xml.validation.Schema</code> is an
* opaque object, most of these actually work with DOM documents that
* contain an XSD.
- * <p>
- * <b>Behavior of {@link #newSchema newSchema()} and {@link
- * #newSchemas newSchemas()}</b>
- * <p>
- * These methods exist to create a {@link javax.xml.validation.Schema}
- * from an XML source. Their basic behavior is to hide the underlying
- * factory objects, similar to {@link ParseUtil} and {@link OutputUtil}.
- * However, also they support the creation of <code>Schema<code> objects
- * from multiple sources, and in this have to deal with a
- * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6198705">
- * bug in the JDK</a> (and also in
- * <a href="http://issues.apache.org/jira/browse/XERCESJ-1130">Xerces</a>)
- * that prevents the combination of schema documents that specify the same
- * namespace (this also prevents <xs:import> from working).
- * <p>
- * Combining schema documents isn't an easy task, which is probably why the
- * bug still isn't fixed. The top-level attributes (those attached to the
- * <code><xs:schema></code> element) are the main problem, as they have
- * to be consistent across the component schema docs. The approach used by
- * {@link #newSchema newSchema()} is to take these attributes from the first
- * source (for {@link #newSchemas newSchemas()}, the first source for a given
- * namespace URI), and apply the following rules:
- * <ul>
- * <li><code>targetNamespace</code>
- * <br>This must be the same for all source documents processed by {@link
- * #newSchema newSchema()}; if not, it throws an exception. By comparison,
- * {@link #newSchemas newSchemas()} will partition source documents by
- * their value, so an incorrect source document will be silently assigned
- * its own <code>Schema</code> object.
- * <li><code>attributeFormDefault</code>, <code>elementFormDefault</code>
- * <br>The first source document defines these attributes for the resulting
- * <code>Schema</code> object. If later sources define different values,
- * those values are ignored. This behavior largely reflects my view of
- * how an XSD should be modularized (with a single <code>xsd:element
- * </code> in the first source, type definitions in subsequent sources),
- * but it is also driven by consistency in instance documents. As a side
- * note, I strongly recommend that <code>elementFormDefault</code> be
- * "qualified", as that allows the use of a default namespace in instance
- * documents.
- * <li>all other attributes
- * <br>Are defined by the first source, and ignored on any subsequent sources.
- * </ul>
*/
public class SchemaUtil
{
- private final static String SCHEMA_NS = XMLConstants.W3C_XML_SCHEMA_NS_URI;
+ private final static String NS_SCHEMA = XMLConstants.W3C_XML_SCHEMA_NS_URI;
private final static String EL_SCHEMA = "schema";
+ private final static String EL_IMPORT = "import";
private final static String ATTR_TARGET_NS = "targetNamespace";
+ private final static String ATTR_IMPORT_NS = "namespace";
+ private final static String ATTR_IMPORT_LOC = "schemaLocation";
/**
@@ -83,34 +48,116 @@
/**
- * Parses the passed input sources to produce a single <code>Schema</code>
- * object. All sources must have the same <code>targetNamespace</code>;
- * other top-level attributes will be taken from the first source.
+ * Parses one or more input sources to produce a <code>Schema</code>
+ * object. Validators produced from this schema will throw an
+ * {@link XmlException} on any error.
+ * <p>
+ * The caller is responsible for ordering the sources so that imported
+ * schemas appear first. This method is unable to combine sources from
+ * the same target namespace; see {@link #combineSchema combineSchema()}
+ * for explanation.
*
+ * @param sources The source schema documents. Note that these are
+ * <code>org.xml.sax.InputSource</code> objects for
+ * consistency with other classes in this library;
+ * not the <code>javax.xml.transform.Source</code>
+ * objects used by <code>SchemaFactory</code>.
+ *
+ * @throws IllegalArgumentException if invoked without any sources.
+ * @throws XmlException if unable to create the schema object.
+ */
+ public static Schema newSchema(InputSource... sources)
+ {
+ return newSchema(newFactory(new ExceptionErrorHandler()), sources);
+ }
+
+
+ /**
+ * Parses one or more input sources to produce a <code>Schema</code>
+ * object from the passed factory. This call is synchronized on the
+ * factory, which the JDK 1.5 docs describe as not threadsafe.
+ * <p>
+ * The caller is responsible for ordering the sources so that imported
+ * schemas appear first. This method is unable to combine sources from
+ * the same target namespace; see {@link #combineSchema combineSchema()}
+ * for explanation.
+ *
+ * @param factory Used to create the schema object.
+ * @param sources The source schema documents. Note that these are
+ * <code>org.xml.sax.InputSource</code> objects for
+ * consistency with other classes in this library;
+ * not the <code>javax.xml.transform.Source</code>
+ * objects used by <code>SchemaFactory</code>.
+ *
+ * @throws IllegalArgumentException if invoked without any sources.
+ * @throws XmlException if unable to parse a source or compile the schema.
+ */
+ public static Schema newSchema(SchemaFactory factory, InputSource... sources)
+ {
+ Document[] parsed = parseSources(sources);
+ try
+ {
+ synchronized (factory)
+ {
+ return factory.newSchema(toDOMSources(parsed));
+ }
+ }
+ catch (SAXException e)
+ {
+ throw new XmlException("unable to generate schema", e);
+ }
+ }
+
+
+ /**
+ * Parses one or more input sources to produce a <code>Schema</code>
+ * object. Unlike {@link #newSchema newSchema()}, this method will
+ * combine source documents with the same target namespace (a workaround
+ * for <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6198705">
+ * JDK 1.5 bug 6198705</a>), order the source documents according to their
+ * dependencies, and also remove external location references from <code>
+ * <xsd:import></code> elements that reference provided sources.
+ * <p>
+ * When combining schema documents with the same namespace, all top-level
+ * attributes (eg, <code>elementFormDefault</code>) come from the first
+ * source specified for the namespace. The <code><xsd:schema></code>
+ * element, and its attributes, are ignored for subsequent sources for
+ * that namespace.
+ * <p>
+ * Sources must contain <code><xsd:import></code> elements for any
+ * referenced schemas. If the sources contain a schema for the specified
+ * target namespace, any <code>schemaLocation</code> specification will
+ * be ignored.
+ *
* @param factory Used to create the schema object. This factory's
* <code>ErrorHandler</code> is also used to report
* errors when parsing the source documents.
- * @param sources The source schema documents.
+ * @param sources The source schema documents. Note that these are
+ * <code>org.xml.sax.InputSource</code> objects for
+ * consistency with other classes in this library;
+ * not the <code>javax.xml.transform.Source</code>
+ * objects used by <code>SchemaFactory</code>.
*
* @throws IllegalArgumentException if invoked without any sources, or if
- * a source does not have the target namespace established by the
- * first source.
- * @throws XmlException if the sources are not combinable, or on any
- * failure that is not handled by the factory's <code>ErrorHandler
- * </code> (including parse errors and local validation of the
- * source root element).
+ * a source does not appear to be an XML Schema document (current
+ * checking is minimal, but that may change).
+ * @throws XmlException on any failure that is not handled by the factory's
+ * <code>ErrorHandler</code> (including parse errors).
*/
- public static Schema newSchema(SchemaFactory factory, InputSource... sources)
+ public static Schema combineSchema(SchemaFactory factory, InputSource... sources)
{
- if (sources.length == 0)
- throw new IllegalArgumentException("must specify at least one source");
-
- Document dom = parse(sources[0]);
- for (int ii = 1 ; ii < sources.length ; ii++)
+ SchemaManager manager = new SchemaManager(parseSources(sources));
+ try
{
- combine(dom, parse(sources[ii]));
+ synchronized (factory)
+ {
+ return factory.newSchema(manager.toDOMSources());
+ }
}
- return generate(factory, dom);
+ catch (SAXException e)
+ {
+ throw new XmlException("unable to generate schema", e);
+ }
}
@@ -119,65 +166,190 @@
//----------------------------------------------------------------------------
/**
- * Parses an XML document and performs some perfunctory checks to verify
- * that it's an XML schema. Perhaps in the future we'll add a "validation
- * level" property that at high levels validates against the "Schema for
- * Schemas"
+ * Parses an array of <code>org.xml.sax.InputSource</code> objects, and
+ * performs some (minimal) level of validation on them. This method is
+ * called by <code>newSchema()</code> and <code>combineSchema()</code>,
+ * and will identify the source that wasn't valid.
+ *
+ * @throws IllegalArgumentException if no sources specified (this is
+ * a common check for callers).
*/
- private static Document parse(InputSource source)
+ public static Document[] parseSources(InputSource[] sources)
{
- Document dom = ParseUtil.parse(source);
- Element root = dom.getDocumentElement();
- if (!DomUtil.isNamed(root, SCHEMA_NS, EL_SCHEMA))
+ if (sources.length == 0)
+ throw new IllegalArgumentException("must specify at least one source");
+
+ Document[] result = new Document[sources.length];
+ for (int ii = 0 ; ii < sources.length ; ii++)
{
- throw new XmlException("invalid root element name: {"
- + root.getNamespaceURI() + "}"
- + DomUtil.getLocalName(root));
+ try
+ {
+ result[ii] = ParseUtil.parse(sources[ii]);
+ }
+ catch (XmlException ee)
+ {
+ throw new XmlException("unable to parse source " + ii, ee.getCause());
+ }
}
- return dom;
+
+ for (int ii = 0 ; ii < result.length ; ii++)
+ {
+ if (!DomUtil.isNamed(result[ii].getDocumentElement(), NS_SCHEMA, EL_SCHEMA))
+ throw new XmlException("source " + ii + " does not appear to be an XSD");
+ }
+
+ return result;
}
+ /**
+ * Wraps an array of DOM documents so that they can be processed by
+ * <code>SchemaFactory</code>.
+ */
+ private static DOMSource[] toDOMSources(Document[] sources)
+ {
+ DOMSource[] result = new DOMSource[sources.length];
+ for (int ii = 0 ; ii < sources.length ; ii++)
+ {
+ result[ii] = new DOMSource(sources[ii]);
+ }
+ return result;
+ }
+
/**
- * Merges the second DOM object into the first, verifying that it (1)
- * claims to be an XML Schema, and (2) has the same root attributes as
- * the first document.
+ * This object is the brains behind {@link #combineSchema}. It is
+ * currently written for the quirks of the 1.5 JDK; if those quirks
+ * are different under 1.6, it will be moved into its own package and
+ * accessed via a factory.
+ * <p>
+ * Defined as protected -- as are internal methods -- so that it can be
+ * tested independently.
*/
- private static void combine(Document dst, Document src)
+ protected static class SchemaManager
{
- Element srcRoot = src.getDocumentElement();
- Element dstRoot = dst.getDocumentElement();
+ private HashMap<String,Document> _documents = new HashMap<String,Document>();
- String srcTargetNS = srcRoot.getAttribute(ATTR_TARGET_NS);
- String dstTargetNS = dstRoot.getAttribute(ATTR_TARGET_NS);
- if (!StringUtils.equals(srcTargetNS, dstTargetNS))
- throw new IllegalArgumentException(
- "cannot combine target namespaces: expected "
- + "\"" + dstTargetNS + "\", was \"" + srcTargetNS + "\"");
+ public SchemaManager(Document[] sources)
+ {
+ for (int ii = 0 ; ii < sources.length ; ii++)
+ {
+ String srcNamespace = sources[ii].getDocumentElement().getAttribute(ATTR_TARGET_NS);
+ Document existing = _documents.get(srcNamespace);
+ if (existing != null)
+ merge(existing, sources[ii]);
+ else
+ _documents.put(srcNamespace, sources[ii]);
+ }
+ }
- for (Element child : DomUtil.getChildren(srcRoot))
+ /**
+ * Returns the ordered set of sources for this manager.
+ */
+ public DOMSource[] toDOMSources()
{
- Node tmp = dst.importNode(child, true);
- dstRoot.appendChild(tmp);
+ TreeSet<Document> ordered = new TreeSet<Document>(new SchemaComparator());
+ for (Document doc : _documents.values())
+ {
+ ordered.add(rebuildImports(doc));
+ }
+
+ return SchemaUtil.toDOMSources(ordered.toArray(new Document[ordered.size()]));
}
+
+ /**
+ * Merges one schema document into another.
+ */
+ protected void merge(Document dst, Document src)
+ {
+ Element dstRoot = dst.getDocumentElement();
+ Element srcRoot = src.getDocumentElement();
+ for (Element child : DomUtil.getChildren(srcRoot))
+ {
+ Node tmp = dst.importNode(child, true);
+ dstRoot.appendChild(tmp);
+ }
+ }
+
+ /**
+ * Rebuilds the <code>import</code> statements for the passed
+ * document, removing duplicates and clearing locations for
+ * namespaces that are known to this manager. Returns the
+ * cleaned document.
+ */
+ protected Document rebuildImports(Document doc)
+ {
+ Map<String,String> imports = new HashMap<String,String>();
+ Element root = doc.getDocumentElement();
+ for (Element imp : DomUtil.getChildren(root, NS_SCHEMA, EL_IMPORT))
+ {
+ String namespace = imp.getAttribute(ATTR_IMPORT_NS);
+ String location = imp.getAttribute(ATTR_IMPORT_LOC);
+ if (_documents.containsKey(namespace))
+ location = null;
+ imports.put(namespace, location);
+ root.removeChild(imp);
+ }
+
+ for (String namespace : imports.keySet())
+ {
+ String location = imports.get(namespace);
+ Element newImport = doc.createElementNS(NS_SCHEMA, EL_IMPORT);
+ newImport.setAttribute(ATTR_IMPORT_NS, namespace);
+ if (location != null)
+ newImport.setAttribute(ATTR_IMPORT_LOC, location);
+ root.insertBefore(newImport, root.getFirstChild());
+ }
+ return doc;
+ }
}
/**
- * Generates a new <code>Schema</code> object from a DOM document.
+ * Compares two schema documents: one schema is greater-than another if
+ * it imports the other's namespace. To impose a total ordering, schemas
+ * that don't have interdependencies are ordered based on their target
+ * namespace URLs, and schemas with no target namespace are greater-than
+ * those with (since they cannot be imported).
+ * <p>
+ * Defined as protected so that it can be tested independently.
*/
- private static Schema generate(SchemaFactory factory, Document dom)
+ protected static class SchemaComparator
+ implements Comparator<Document>
{
- try
+ public int compare(Document o1, Document o2)
{
- synchronized (factory)
+ if (o1 == o2)
+ return 0;
+
+ Element root1 = o1.getDocumentElement();
+ String namespace1 = root1.getAttribute(ATTR_TARGET_NS);
+
+ Element root2 = o2.getDocumentElement();
+ String namespace2 = root2.getAttribute(ATTR_TARGET_NS);
+
+ if (namespace1.equals(namespace2))
+ return 0;
+ else if ("".equals(namespace1))
+ return 1;
+ else if ("".equals(namespace2))
+ return -1;
+
+ if (isImportedBy(namespace1, root2))
+ return -1;
+ else if (isImportedBy(namespace2, root1))
+ return 1;
+
+ return namespace1.compareTo(namespace2);
+ }
+
+ private boolean isImportedBy(String namespace, Element root)
+ {
+ for (Element imp : DomUtil.getChildren(root, NS_SCHEMA, EL_IMPORT))
{
- return factory.newSchema(new DOMSource(dom));
+ if (namespace.equals(imp.getAttribute(ATTR_IMPORT_NS)))
+ return true;
}
+ return false;
}
- catch (SAXException e)
- {
- throw new XmlException("unable to generate schema", e);
- }
}
}
Modified: trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java
===================================================================
--- trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java 2008-12-28 00:46:35 UTC (rev 61)
+++ trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java 2008-12-28 15:56:10 UTC (rev 62)
@@ -1,12 +1,17 @@
package net.sf.practicalxml;
import java.io.StringReader;
+import java.util.Comparator;
+import java.util.List;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
+import net.sf.practicalxml.SchemaUtil.SchemaManager;
import net.sf.practicalxml.util.ExceptionErrorHandler;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
@@ -15,13 +20,7 @@
extends AbstractTestCase
{
//----------------------------------------------------------------------------
-// Support Code
-//----------------------------------------------------------------------------
-
-
-
-//----------------------------------------------------------------------------
-// Test Cases
+// Test Cases for public methods
//
// Note: since we can't examine a schema once it's compiled, we need to
// verify newSchema() operation with actual instance docs ... and
@@ -61,9 +60,57 @@
+ "<bargle>test</bargle>"
+ "</foo>";
+ Schema schema = SchemaUtil.newSchema(new InputSource(new StringReader(xsd)));
+
+ assertNotNull(schema);
+ ParseUtil.validatingParse(new InputSource(new StringReader(xml)),
+ schema,
+ new ExceptionErrorHandler());
+ }
+
+
+ public void testNewSchemaMultipleNamepaces() throws Exception
+ {
+ String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://foo.example.com'"
+ + " xmlns:bar='http://bar.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:import namespace='http://bar.example.com'/>"
+ + "<xsd:element name='foo' type='bar:FooType'/>"
+ + "</xsd:schema>";
+ String xsd2 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://bar.example.com'"
+ + " xmlns:baz='http://baz.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:import namespace='http://baz.example.com'/>"
+ + "<xsd:complexType name='FooType'>"
+ + "<xsd:sequence>"
+ + "<xsd:element name='argle' type='xsd:integer'/>"
+ + "<xsd:element name='bargle' type='baz:BarType'/>"
+ + "</xsd:sequence>"
+ + "</xsd:complexType>"
+ + "</xsd:schema>";
+ String xsd3 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://baz.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:simpleType name='BarType'>"
+ + "<xsd:restriction base='xsd:string'>"
+ + "</xsd:restriction>"
+ + "</xsd:simpleType>"
+ + "</xsd:schema>";
+
+ String xml = "<foo xmlns='http://foo.example.com'>"
+ + "<argle xmlns='http://bar.example.com'>12</argle>"
+ + "<bargle xmlns='http://bar.example.com'>12</bargle>"
+ + "</foo>";
+
Schema schema = SchemaUtil.newSchema(
- SchemaUtil.newFactory(new ExceptionErrorHandler()),
- new InputSource(new StringReader(xsd)));
+ new InputSource(new StringReader(xsd3)),
+ new InputSource(new StringReader(xsd2)),
+ new InputSource(new StringReader(xsd1)));
assertNotNull(schema);
ParseUtil.validatingParse(new InputSource(new StringReader(xml)),
@@ -72,8 +119,72 @@
}
- public void testNewSchemaMultipleSources() throws Exception
+ public void testNewSchemaFailNoSources() throws Exception
{
+ try
+ {
+ SchemaUtil.newSchema();
+ fail("no sources, no exception");
+ }
+ catch (IllegalArgumentException ee)
+ {
+ // success
+ }
+ }
+
+
+ public void testNewSchemaFailInvalidDocument() throws Exception
+ {
+ // looks right, but no namespace definition
+ String xsd = "<schema>"
+ + "<element name='foo' type='FooType'/>"
+ + "<complexType name='FooType'>"
+ + "<sequence>"
+ + "<element name='argle' type='xsd:integer'/>"
+ + "</sequence>"
+ + "</complexType>"
+ + "</schema>";
+
+ try
+ {
+ SchemaUtil.newSchema(new InputSource(new StringReader(xsd)));
+ fail("created schema from an invalid document");
+ }
+ catch (XmlException ee)
+ {
+ assertTrue(ee.getMessage().contains("source 0"));
+ }
+ }
+
+
+ public void testNewSchemaFailUnparsableSource() throws Exception
+ {
+ String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://foo.example.com'"
+ + " xmlns:bar='http://bar.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:import namespace='http://bar.example.com'/>"
+ + "<xsd:element name='foo' type='bar:FooType'/>"
+ + "</xsd:schema>";
+ String xsd2 = "this isn't XML";
+
+ try
+ {
+ SchemaUtil.newSchema(
+ new InputSource(new StringReader(xsd1)),
+ new InputSource(new StringReader(xsd2)));
+ fail("parsed an invalid XML document");
+ }
+ catch (XmlException ee)
+ {
+ assertTrue(ee.getMessage().contains("source 1"));
+ }
+ }
+
+
+ public void testCombineSchemaMultipleSourceNoNamespace() throws Exception
+ {
String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>"
+ "<xsd:element name='foo' type='FooType'/>"
+ "</xsd:schema>";
@@ -97,7 +208,7 @@
+ "<bargle>test</bargle>"
+ "</foo>";
- Schema schema = SchemaUtil.newSchema(
+ Schema schema = SchemaUtil.combineSchema(
SchemaUtil.newFactory(new ExceptionErrorHandler()),
new InputSource(new StringReader(xsd1)),
new InputSource(new StringReader(xsd2)),
@@ -110,28 +221,34 @@
}
- public void testNewSchemaSingleSourceWithNamespace() throws Exception
+ public void testCombineSchemaMultipleSourceSingleNamespace() throws Exception
{
- String xsd = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
- + " xmlns='http://foo.example.com'"
- + " targetNamespace='http://foo.example.com'"
- + " elementFormDefault='qualified'"
- + ">"
- + "<xsd:element name='foo' type='FooType'/>"
- + "<xsd:complexType name='FooType'>"
- + "<xsd:sequence>"
- + "<xsd:element name='argle' type='xsd:integer'/>"
- + "</xsd:sequence>"
- + "</xsd:complexType>"
- + "</xsd:schema>";
+ String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " xmlns='http://foo.example.com'"
+ + " targetNamespace='http://foo.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:element name='foo' type='FooType'/>"
+ + "</xsd:schema>";
+ String xsd2 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://foo.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:complexType name='FooType'>"
+ + "<xsd:sequence>"
+ + "<xsd:element name='argle' type='xsd:integer'/>"
+ + "</xsd:sequence>"
+ + "</xsd:complexType>"
+ + "</xsd:schema>";
String xml = "<foo xmlns='http://foo.example.com'>"
+ "<argle>12</argle>"
+ "</foo>";
- Schema schema = SchemaUtil.newSchema(
+ Schema schema = SchemaUtil.combineSchema(
SchemaUtil.newFactory(new ExceptionErrorHandler()),
- new InputSource(new StringReader(xsd)));
+ new InputSource(new StringReader(xsd1)),
+ new InputSource(new StringReader(xsd2)));
assertNotNull(schema);
ParseUtil.validatingParse(new InputSource(new StringReader(xml)),
@@ -140,34 +257,50 @@
}
- public void testNewSchemaMultipleSourceWithNamespace() throws Exception
+ public void testCombineSchemaMultipleSourceMultipleNamepaces() throws Exception
{
String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
- + " xmlns='http://foo.example.com'"
+ " targetNamespace='http://foo.example.com'"
+ + " xmlns:bar='http://bar.example.com'"
+ " elementFormDefault='qualified'"
+ ">"
- + "<xsd:element name='foo' type='FooType'/>"
+ + "<xsd:import namespace='http://bar.example.com'/>"
+ + "<xsd:element name='foo' type='bar:FooType'/>"
+ "</xsd:schema>";
String xsd2 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
- + " targetNamespace='http://foo.example.com'"
+ + " targetNamespace='http://bar.example.com'"
+ + " xmlns:baz='http://baz.example.com'"
+ " elementFormDefault='qualified'"
+ ">"
+ + "<xsd:import namespace='http://baz.example.com'/>"
+ "<xsd:complexType name='FooType'>"
+ "<xsd:sequence>"
- + "<xsd:element name='argle' type='xsd:integer'/>"
+ + "<xsd:element name='argle' type='xsd:integer'/>"
+ + "<xsd:element name='bargle' type='baz:BarType'/>"
+ "</xsd:sequence>"
+ "</xsd:complexType>"
+ "</xsd:schema>";
+ String xsd3 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://baz.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:simpleType name='BarType'>"
+ + "<xsd:restriction base='xsd:string'>"
+ + "</xsd:restriction>"
+ + "</xsd:simpleType>"
+ + "</xsd:schema>";
String xml = "<foo xmlns='http://foo.example.com'>"
- + "<argle>12</argle>"
+ + "<argle xmlns='http://bar.example.com'>12</argle>"
+ + "<bargle xmlns='http://bar.example.com'>12</bargle>"
+ "</foo>";
- Schema schema = SchemaUtil.newSchema(
+ // note: these sources are intentionally out-of-order
+ Schema schema = SchemaUtil.combineSchema(
SchemaUtil.newFactory(new ExceptionErrorHandler()),
new InputSource(new StringReader(xsd1)),
- new InputSource(new StringReader(xsd2)));
+ new InputSource(new StringReader(xsd2)),
+ new InputSource(new StringReader(xsd3)));
assertNotNull(schema);
ParseUtil.validatingParse(new InputSource(new StringReader(xml)),
@@ -176,37 +309,160 @@
}
- public void testNewSchemaFailMultipleNamepaces() throws Exception
+//----------------------------------------------------------------------------
+// Test cases for internals
+//----------------------------------------------------------------------------
+
+ public void testSchemaManagerMerge() throws Exception
{
- String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
- + " xmlns:foo='http://foo.example.com'"
+ Document[] docs = new Document[]
+ {
+ ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ " targetNamespace='http://foo.example.com'"
+ + ">"
+ + "<xsd:import namespace='http://bar.example.com'"
+ + " schemaLocation='http://bar.example.com'/>"
+ + "<xsd:element name='foo' type='bar:FooType'/>"
+ + "</xsd:schema>"),
+ ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://foo.example.com'"
+ + ">"
+ + "<xsd:complexType name='FooType'>"
+ + "<xsd:sequence>"
+ + "<xsd:element name='argle' type='xsd:integer'/>"
+ + "</xsd:sequence>"
+ + "</xsd:complexType>"
+ + "</xsd:schema>")
+ };
+
+ Element doc0Root = docs[0].getDocumentElement();
+ List<Element> childrenBeforeManagement = DomUtil.getChildren(doc0Root);
+ assertEquals(2, childrenBeforeManagement.size());
+
+ new SchemaUtil.SchemaManager(docs);
+ List<Element> childrenAfterManagement = DomUtil.getChildren(doc0Root);
+ assertEquals(3, childrenAfterManagement.size());
+ }
+
+
+ public void testSchemaManagerImportRebuild() throws Exception
+ {
+ Document[] docs = new Document[]
+ {
+ ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://foo.example.com'"
+ + ">"
+ + "<xsd:import namespace='http://bar.example.com'"
+ + " schemaLocation='http://bar.example.com'/>"
+ + "<xsd:element name='foo' type='bar:FooType'/>"
+ + "</xsd:schema>"),
+ ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://bar.example.com'"
+ + ">"
+ + "<xsd:complexType name='FooType'>"
+ + "<xsd:sequence>"
+ + "<xsd:element name='argle' type='xsd:integer'/>"
+ + "</xsd:sequence>"
+ + "</xsd:complexType>"
+ + "</xsd:schema>")
+ };
+ SchemaManager manager = new SchemaUtil.SchemaManager(docs);
+
+ Document rebuilt = manager.rebuildImports(docs[0]);
+ List<Element> imports = DomUtil.getChildren(
+ rebuilt.getDocumentElement(),
+ "http://www.w3.org/2001/XMLSchema",
+ "import");
+ assertEquals(1, imports.size());
+
+ Element imp = imports.get(0);
+ assertEquals("http://bar.example.com", imp.getAttribute("namespace"));
+ assertEquals("", imp.getAttribute("schemaLocation"));
+ }
+
+
+ public void testSchemaComparator() throws Exception
+ {
+ Document doc1 = ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://foo.example.com'"
+ " elementFormDefault='qualified'"
+ ">"
- + "<xsd:element name='foo' type='foo:FooType'/>"
- + "</xsd:schema>";
- String xsd2 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + "<xsd:import namespace='http://bar.example.com'/>"
+ + "<xsd:element name='foo' type='bar:FooType'/>"
+ + "</xsd:schema>");
+ Document doc1b = ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://foo.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:import namespace='http://bar.example.com'/>"
+ + "<xsd:element name='foo' type='bar:FooType'/>"
+ + "</xsd:schema>");
+ Document doc2 = ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ " targetNamespace='http://bar.example.com'"
+ + " xmlns:baz='http://bar.example.com'"
+ " elementFormDefault='qualified'"
+ ">"
+ + "<xsd:import namespace='http://baz.example.com'/>"
+ "<xsd:complexType name='FooType'>"
+ "<xsd:sequence>"
+ "<xsd:element name='argle' type='xsd:integer'/>"
+ + "<xsd:element name='bargle' type='baz:BarType'/>"
+ "</xsd:sequence>"
+ "</xsd:complexType>"
- + "</xsd:schema>";
+ + "</xsd:schema>");
+ Document doc3 = ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " targetNamespace='http://baz.example.com'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:simpleType name='BarType'>"
+ + "<xsd:restriction base='xsd:string'>"
+ + "</xsd:restriction>"
+ + "</xsd:simpleType>"
+ + "</xsd:schema>");
+ Document doc4 = ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:simpleType name='ZippyType'>"
+ + "<xsd:restriction base='xsd:string'>"
+ + "</xsd:restriction>"
+ + "</xsd:simpleType>"
+ + "</xsd:schema>");
+ Document doc4b = ParseUtil.parse(
+ "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'"
+ + " elementFormDefault='qualified'"
+ + ">"
+ + "<xsd:simpleType name='ZippyType'>"
+ + "<xsd:restriction base='xsd:string'>"
+ + "</xsd:restriction>"
+ + "</xsd:simpleType>"
+ + "</xsd:schema>");
- try
- {
- SchemaUtil.newSchema(
- SchemaUtil.newFactory(new ExceptionErrorHandler()),
- new InputSource(new StringReader(xsd1)),
- new InputSource(new StringReader(xsd2)));
- fail("combined schemas with different target namespaces");
- }
- catch (IllegalArgumentException ee)
- {
- // success
- }
+ Comparator<Document> comparator = new SchemaUtil.SchemaComparator();
+
+ // import relationship
+ assertTrue(comparator.compare(doc1, doc2) > 0);
+ assertTrue(comparator.compare(doc2, doc1) < 0);
+
+ // target namespace
+ assertTrue(comparator.compare(doc1, doc3) > 0);
+ assertTrue(comparator.compare(doc3, doc1) < 0);
+
+ // target namespace versus none
+ assertTrue(comparator.compare(doc1, doc4) < 0);
+ assertTrue(comparator.compare(doc4, doc1) > 0);
+
+ // equality
+ assertTrue(comparator.compare(doc1, doc1) == 0);
+ assertTrue(comparator.compare(doc1, doc1b) == 0);
+ assertTrue(comparator.compare(doc4, doc4b) == 0);
}
}
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|