practicalxml-commits Mailing List for Practical XML (Page 10)
Brought to you by:
kdgregory
You can subscribe to this list here.
2008 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
(6) |
Nov
(4) |
Dec
(35) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2009 |
Jan
(5) |
Feb
|
Mar
|
Apr
(7) |
May
|
Jun
|
Jul
(12) |
Aug
(24) |
Sep
(39) |
Oct
(16) |
Nov
(4) |
Dec
(7) |
2010 |
Jan
(10) |
Feb
|
Mar
(2) |
Apr
(1) |
May
|
Jun
|
Jul
|
Aug
(1) |
Sep
|
Oct
|
Nov
(4) |
Dec
(3) |
2011 |
Jan
(1) |
Feb
|
Mar
(1) |
Apr
(1) |
May
(3) |
Jun
|
Jul
|
Aug
(1) |
Sep
(10) |
Oct
(1) |
Nov
(1) |
Dec
(7) |
2012 |
Jan
(1) |
Feb
|
Mar
|
Apr
(1) |
May
(9) |
Jun
|
Jul
(5) |
Aug
(6) |
Sep
|
Oct
(1) |
Nov
|
Dec
|
2013 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(16) |
Jul
|
Aug
(6) |
Sep
(10) |
Oct
|
Nov
(2) |
Dec
|
2014 |
Jan
(5) |
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
(2) |
Aug
|
Sep
|
Oct
(6) |
Nov
|
Dec
|
2015 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(5) |
2016 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
(1) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
From: Auto-Generated S. C. M. <pra...@li...> - 2009-04-30 13:04:44
|
Revision: 82 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=82&view=rev Author: kdgregory Date: 2009-04-30 13:04:43 +0000 (Thu, 30 Apr 2009) Log Message: ----------- JavaDoc wants package.html to have HTML tags, was breaking site build ... who'da thunk Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/internal/package.html Modified: trunk/src/main/java/net/sf/practicalxml/internal/package.html =================================================================== --- trunk/src/main/java/net/sf/practicalxml/internal/package.html 2009-04-30 12:08:58 UTC (rev 81) +++ trunk/src/main/java/net/sf/practicalxml/internal/package.html 2009-04-30 13:04:43 UTC (rev 82) @@ -1,4 +1,8 @@ -This package contains classes used internally by the PracticalXML library. -Applications should not rely on the API of these classes, or indeed of their -continued existence. In most cases, there's a better alternative in a third- -party library. \ No newline at end of file +<html> +<body> + This package contains classes used internally by the PracticalXML library. + Applications should not rely on the API of these classes, or indeed of their + continued existence. In most cases, there's a better alternative in a third- + party library. +</body> +</html> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-04-30 12:09:07
|
Revision: 81 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=81&view=rev Author: kdgregory Date: 2009-04-30 12:08:58 +0000 (Thu, 30 Apr 2009) Log Message: ----------- update logo usage per new SF policy Modified Paths: -------------- trunk/pom.xml trunk/src/site/site.xml Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2009-04-28 17:54:17 UTC (rev 80) +++ trunk/pom.xml 2009-04-30 12:08:58 UTC (rev 81) @@ -83,7 +83,7 @@ <artifactId>maven-javadoc-plugin</artifactId> <configuration> <bottom> - <a href="http://sourceforge.net/"> + <a href="http://sourceforge.net/projects/practicalxml/"> <img src="http://sflogo.sourceforge.net/sflogo.php?group_id=234884&type=3"> </a> </bottom> Modified: trunk/src/site/site.xml =================================================================== --- trunk/src/site/site.xml 2009-04-28 17:54:17 UTC (rev 80) +++ trunk/src/site/site.xml 2009-04-30 12:08:58 UTC (rev 81) @@ -7,7 +7,7 @@ href="http://maven.apache.org/" img="http://maven.apache.org/images/logos/maven-feather.png"/> <logo name="SourceForge" - href="http://sourceforge.net/" + href="http://sourceforge.net/projects/practicalxml/" img="http://sflogo.sourceforge.net/sflogo.php?group_id=234884&type=3"/> </poweredBy> </project> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-04-28 17:54:28
|
Revision: 80 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=80&view=rev Author: kdgregory Date: 2009-04-28 17:54:17 +0000 (Tue, 28 Apr 2009) Log Message: ----------- XmlBuilder: output routines now properly handle comments (using SAX extension class) Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java Modified: trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java 2009-04-27 12:41:46 UTC (rev 79) +++ trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java 2009-04-28 17:54:17 UTC (rev 80) @@ -16,6 +16,9 @@ import org.w3c.dom.Comment; import org.w3c.dom.Element; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; +import org.xml.sax.ext.LexicalHandler; /** @@ -40,4 +43,14 @@ Comment node = parent.getOwnerDocument().createComment(_content); parent.appendChild(node); } + + + @Override + protected void toSAX(ContentHandler handler) throws SAXException + { + if (handler instanceof LexicalHandler) + { + ((LexicalHandler)handler).comment(_content.toCharArray(), 0, _content.length()); + } + } } Modified: trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java 2009-04-27 12:41:46 UTC (rev 79) +++ trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java 2009-04-28 17:54:17 UTC (rev 80) @@ -85,8 +85,10 @@ /** - * Invokes the passed <code>ContentHandler</code> for this element - * and its children. + * Invokes the passed <code>ContentHandler</code> for this element and + * its children. Note that the implementation class must also implement + * <code>LexicalHandler</code> to receive events from all nodes in the + * tree (particularly comments). */ @Override protected void toSAX(ContentHandler handler) @@ -106,12 +108,6 @@ * not insert whitespace between elements. Note that you <em>must</em> * use UTF-8 encoding or add a prologue that specifies encoding when * writing this string to a stream. - * <p> - * <em>Warning:</em> - * This method uses a SAX transformer, to minimize footprint. However, - * SAX does not support comment modes, so they will be silently dropped. - * If they are important to you, call {@link #toDOM} and use {@link - * net.sf.practicalxml.OutputUtil#compactString} to generate output. */ @Override public String toString() @@ -128,12 +124,6 @@ * This is the best choice for writing log output. If you write this string * to a stream, you <em>must</em> use UTF-8 encoding or attach a prologue * that specifies the encoding used. - * <p> - * <em>Warning:</em> - * This method uses a SAX transformer, to minimize footprint. However, - * SAX does not support comment modes, so they will be silently dropped. - * If they are important to you, call {@link #toDOM} and use {@link - * net.sf.practicalxml.OutputUtil#indentedString} to generate output. */ public String toString(int indentSize) { @@ -147,12 +137,6 @@ * <p> * This is the best choice for writing XML that will be read by another * party. - * <p> - * <em>Warning:</em> - * This method uses a SAX transformer, to minimize footprint. However, - * SAX does not support comment modes, so they will be silently dropped. - * If they are important to you, call {@link #toDOM} and use {@link - * net.sf.practicalxml.OutputUtil#compactStream} to generate output. */ public void toStream(OutputStream out) { @@ -164,12 +148,6 @@ * Writes the tree rooted at this element to an <code>OutputStream</code>, * using a specified encoding, without a prologue or whitepspace between * nodes. - * <p> - * <em>Warning:</em> - * This method uses a SAX transformer, to minimize footprint. However, - * SAX does not support comment modes, so they will be silently dropped. - * If they are important to you, call {@link #toDOM} and use {@link - * net.sf.practicalxml.OutputUtil#compactStream} to generate output. */ public void toStream(OutputStream out, String encoding) { Modified: trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java 2009-04-27 12:41:46 UTC (rev 79) +++ trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java 2009-04-28 17:54:17 UTC (rev 80) @@ -97,9 +97,9 @@ /** * Creates a comment node. * <p> - * <em>Warning</em>: - * Comment nodes are not reported by SAX sources. If comments are - * important to you, convert to DOM before serialization. + * <em>Warning:</em> + * Comment nodes are only reported to SAX content handlers that also + * implement <code>org.xml.sax.ext.LexicalHandler</code>. */ public static Node comment(String text) { Modified: trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java 2009-04-27 12:41:46 UTC (rev 79) +++ trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java 2009-04-28 17:54:17 UTC (rev 80) @@ -32,6 +32,7 @@ import org.w3c.dom.ProcessingInstruction; import org.xml.sax.Attributes; import org.xml.sax.ContentHandler; +import org.xml.sax.ext.LexicalHandler; import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; @@ -72,7 +73,7 @@ { return (ContentHandler)Proxy.newProxyInstance( ContentHandler.class.getClassLoader(), - new Class[] { ContentHandler.class }, + new Class[] { ContentHandler.class, LexicalHandler.class }, this); } @@ -82,7 +83,7 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - // this is a hack for characters + // this is a hack for characters() and comment() for (int ii = 0 ; ii < args.length ; ii++) { if (args[ii] instanceof char[]) @@ -276,11 +277,11 @@ // note: ContentHandler knows nothing of comments MockContentHandler handler = new MockContentHandler(); node.toSAX(handler.getHandler()); - handler.assertInvocationSequence("startElement", "endElement"); + handler.assertInvocationSequence("startElement", "comment", "endElement"); handler.assertInvocation(0, "startElement", null, "foo", "foo"); + handler.assertInvocation(1, "comment", "bar", 0, 3); - -// assertEquals("<foo><!--bar--></foo>", node.toString()); + assertEquals("<foo><!--bar--></foo>", node.toString()); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-04-27 12:41:56
|
Revision: 79 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=79&view=rev Author: kdgregory Date: 2009-04-27 12:41:46 +0000 (Mon, 27 Apr 2009) Log Message: ----------- OutputUtil: added support for XMLReader as input source XmlBuilder: use SAX for output (lowers footprint, discards comments) support Processing Instruction nodes (not that anyone uses them) added better package comment Modified Paths: -------------- trunk/pom.xml trunk/src/main/java/net/sf/practicalxml/OutputUtil.java trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java trunk/src/main/java/net/sf/practicalxml/builder/Node.java trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java trunk/src/main/java/net/sf/practicalxml/builder/package.html trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java Added Paths: ----------- trunk/src/main/java/net/sf/practicalxml/builder/PINode.java Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/pom.xml 2009-04-27 12:41:46 UTC (rev 79) @@ -5,7 +5,7 @@ <groupId>net.sf.practicalxml</groupId> <artifactId>practicalxml</artifactId> <packaging>jar</packaging> - <version>1.0.1</version> + <version>1.0.2</version> <name>practicalxml</name> <url>http://sourceforge.net/projects/practicalxml/</url> Modified: trunk/src/main/java/net/sf/practicalxml/OutputUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/OutputUtil.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/OutputUtil.java 2009-04-27 12:41:46 UTC (rev 79) @@ -25,10 +25,12 @@ import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXSource; import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.xml.sax.XMLReader; /** @@ -42,7 +44,8 @@ /** * A simple <code>toString()</code> for an element, using the format * "<code>{<i>NSURI</i>}<i>LOCALNAME</i></code>"; if the element has no - * namespace, the brackets remain but are empty. + * namespace, the brackets remain but are empty. This is typically used + * for debugging. */ public static String elementToString(Element elem) { @@ -51,12 +54,8 @@ /** - * Debug dump of the tree rooted at the specified element. Each line holds - * one element, - * - * - * @param elem - * @param indent + * Debug dump of the e rooted at the specified element. Each line holds + * one element, and elements are formatted per {@link #elementToString}. */ public static String treeToString(Element elem, int indent) { @@ -68,7 +67,6 @@ * Writes a DOM document to a simple string format, without a prologue or * whitespace between elements. * <p> - * * Do not simply write this string to a file unless you use UTF-8 encoding * or attach a prologue that specifies your actual encoding. * @@ -84,10 +82,38 @@ /** + * Writes XML in a simple string format, without prologue or whitespace + * between elements, using the passed <code>XMLReader</code> to generate + * a stream of SAX events. + * <p> + * The transformer will call the reader's <code>setContentHandler()</code> + * method, followed by <code>parse()</code>. In the latter method, the + * reader must invoke the content handler's event methods in the correct + * order: at the very least, <code>startDocument() </code>, followed by + * <code>startElement()</code> and <code>endElement()</code> for the root + * element, finishing with <code>endDocument()</code>. Note that SAX does + * not support all DOM node types: in particular, there are no comments. + * <p> + * Do not simply write this string to a file unless you use UTF-8 encoding + * or attach a prologue that specifies your actual encoding. + * + * @param reader Provides a source of SAX events for the transformer. + */ + public static String compactString(XMLReader reader) + { + StringWriter out = new StringWriter(); + new TransformHelper() + .transform(new SAXSource(reader, null), new StreamResult(out)); + return out.toString(); + } + + + /** * Writes a DOM document to a string format, with indenting between - * elements but without a prologue. Do not simply write this string - * to a file unless you use UTF-8 encoding or attach a prologue that - * specifies the encoding. + * elements but without a prologue. + * <p> + * Do not simply write this string to a file unless you use UTF-8 encoding + * or attach a prologue that specifies the encoding. * * @param dom The DOM tree to be output. * @param indentSize The number of spaces to indent each level of the @@ -110,9 +136,45 @@ /** - * Writes a DOM document to a stream, without a prologue or whitespace - * between elements, and using UTF-8 encoding. + * Writes XML in a simple string format, without prologue or whitespace + * between elements, using the passed <code>XMLReader</code> to generate + * a stream of SAX events. + * <p> + * The transformer will call the reader's <code>setContentHandler()</code> + * method, followed by <code>parse()</code>. In the latter method, the + * reader must invoke the content handler's event methods in the correct + * order: at the very least, <code>startDocument() </code>, followed by + * <code>startElement()</code> and <code>endElement()</code> for the root + * element, finishing with <code>endDocument()</code>. Note that SAX does + * not support all DOM node types: in particular, there are no comments. + * <p> + * Do not simply write this string to a file unless you use UTF-8 encoding + * or attach a prologue that specifies the encoding. * + * @param reader Provides a source of SAX events for the transformer. + * @param indentSize The number of spaces to indent each level of the + * tree. Indentation is <em>best effort</em>: the + * <code>javax.transform</code> API does not provide + * any way to set indent level, so we use JDK-specific + * features to achieve this, <em>where available</em>. + * Note also that indenting will cause problems with + * elements that contain mixed content, particularly + * if the text elements cannot be trimmed. + */ + public static String indentedString(XMLReader reader, int indentSize) + { + StringWriter out = new StringWriter(); + new TransformHelper() + .setIndent(indentSize) + .transform(new SAXSource(reader, null), new StreamResult(out)); + return out.toString(); + } + + + /** + * Writes a DOM document to a stream using UTF-8 encoding, without a prologue + * or whitespace between elements. + * * @param dom The DOM tree to be output. * @param stream The output stream. This stream will be flushed by * this method, but will <em>not</em> be closed. @@ -126,6 +188,31 @@ /** + * Writes XML to a stream using UTF-8 encoding, without prologue or + * whitespace between elements, using the passed <code>XMLReader</code> + * to generate a stream of SAX events. + * <p> + * The transformer will call the reader's <code>setContentHandler()</code> + * method, followed by <code>parse()</code>. In the latter method, the + * reader must invoke the content handler's event methods in the correct + * order: at the very least, <code>startDocument() </code>, followed by + * <code>startElement()</code> and <code>endElement()</code> for the root + * element, finishing with <code>endDocument()</code>. Note that SAX does + * not support all DOM node types: in particular, there are no comments. + * + * @param reader Provides a source of SAX events for the transformer. + * @param stream The output stream. This stream will be flushed by + * this method, but will <em>not</em> be closed. + */ + public static void compactStream(XMLReader reader, OutputStream stream) + { + new TransformHelper() + .transform(new SAXSource(reader, null), new StreamResult(stream)); + flushStream(stream); + } + + + /** * Writes a DOM document to a stream using the specified encoding, without * whitespace between elements, but <em>with</em> a prologue that specifes * the encoding. @@ -144,6 +231,32 @@ /** + * Writes XML to a stream using the specified encoding, without prologue or + * whitespace between elements, using the passed <code>XMLReader</code> + * to generate a stream of SAX events. + * <p> + * The transformer will call the reader's <code>setContentHandler()</code> + * method, followed by <code>parse()</code>. In the latter method, the + * reader must invoke the content handler's event methods in the correct + * order: at the very least, <code>startDocument() </code>, followed by + * <code>startElement()</code> and <code>endElement()</code> for the root + * element, finishing with <code>endDocument()</code>. Note that SAX does + * not support all DOM node types: in particular, there are no comments. + * + * @param reader Provides a source of SAX events for the transformer. + * @param stream The output stream. This stream will be flushed by + * this method, but will <em>not</em> be closed. + */ + public static void compactStream(XMLReader reader, OutputStream stream, String encoding) + { + new TransformHelper() + .setPrologue(encoding) + .transform(new SAXSource(reader, null), new StreamResult(stream)); + flushStream(stream); + } + + + /** * This object does the actual transformation work; the exposed static * methods create and configure an instance to do their job. If you * need finer control over output, you can do the same: call the various Modified: trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java 2009-04-27 12:41:46 UTC (rev 79) @@ -15,6 +15,7 @@ package net.sf.practicalxml.builder; import org.w3c.dom.Element; +import org.xml.sax.helpers.AttributesImpl; /** @@ -24,16 +25,18 @@ extends Node implements java.io.Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; private String _nsUri; private String _qname; + private String _lclName; private String _value; public AttributeNode(String nsUri, String qname, String value) { _nsUri = nsUri; _qname = qname; + _lclName = getLocalName(qname); _value = value; } @@ -46,4 +49,14 @@ else parent.setAttributeNS(_nsUri, _qname, _value); } + + + /** + * Helper method to produce a SAX <code>Attributes</code> object. This + * is called by ElementNode. + */ + protected void appendToAttributes(AttributesImpl attrs) + { + attrs.addAttribute(_nsUri, _lclName, _qname, "", _value); + } } Modified: trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java 2009-04-27 12:41:46 UTC (rev 79) @@ -18,6 +18,9 @@ import org.w3c.dom.Element; +/** + * Holds a comment. + */ public class CommentNode extends Node { Modified: trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java 2009-04-27 12:41:46 UTC (rev 79) @@ -14,71 +14,130 @@ package net.sf.practicalxml.builder; +import java.io.IOException; import java.io.OutputStream; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.helpers.AttributesImpl; +import org.xml.sax.helpers.XMLFilterImpl; import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.OutputUtil; +/** + * The primary class for building XML trees and converting them to different + * JAXP-centric forms. Callers should not create instances of this class + * directly; instead use the static factory methods in {@link XmlBuilder}. + */ public final class ElementNode extends Node { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 2L; - private String _nsUri; - private String _qname; - private List<Node> _children; + private String _nsUri; + private String _qname; + private String _lclName; + private List<AttributeNode> _attribs = new ArrayList<AttributeNode>(); + private List<Node> _children = new ArrayList<Node>(); + ElementNode(String nsUri, String qname, Node... children) { _nsUri = nsUri; _qname = qname; - _children = new ArrayList<Node>(Arrays.asList(children)); + _lclName = getLocalName(qname); + for (Node child : children) + addChild(child); } /** - * Adds a child node -- of any type -- to this element. + * Adds a child node -- of any type -- to this element. Returns this as + * a convenience to caller, allowing calls to be chained. */ public ElementNode addChild(Node child) { - if (child != null) + if (child instanceof AttributeNode) + _attribs.add((AttributeNode)child); + else if (child != null) _children.add(child); return this; } /** + * Generates a new DOM document with this element as the root. + */ + public Document toDOM() + { + Element root = DomUtil.newDocument(_nsUri, _qname); + appendChildren(root); + return root.getOwnerDocument(); + } + + + /** + * Invokes the passed <code>ContentHandler</code> for this element + * and its children. + */ + @Override + protected void toSAX(ContentHandler handler) + throws SAXException + { + handler.startElement(_nsUri, _lclName, _qname, getAttributes()); + for (Node child : _children) + { + child.toSAX(handler); + } + handler.endElement(_nsUri, _lclName, _qname); + } + + + /** * Generates an XML string, where this node is the root element. Does - * not insert whitespace between elements. + * not insert whitespace between elements. Note that you <em>must</em> + * use UTF-8 encoding or add a prologue that specifies encoding when + * writing this string to a stream. * <p> - * If you write this string to a file, you <em>must</em> use UTF-8 - * encoding or attach a prologue that specifies the encoding used. + * <em>Warning:</em> + * This method uses a SAX transformer, to minimize footprint. However, + * SAX does not support comment modes, so they will be silently dropped. + * If they are important to you, call {@link #toDOM} and use {@link + * net.sf.practicalxml.OutputUtil#compactString} to generate output. */ @Override public String toString() { - return OutputUtil.compactString(toDOM()); + return OutputUtil.compactString(new SerializationHelper()); } /** * Generates an XML string, where this node is the root element. Inserts - * whitespace between nodes, with the specified indent size. + * whitespace between nodes, along with newlines and the specified indent + * between elements. * <p> * This is the best choice for writing log output. If you write this string * to a stream, you <em>must</em> use UTF-8 encoding or attach a prologue * that specifies the encoding used. + * <p> + * <em>Warning:</em> + * This method uses a SAX transformer, to minimize footprint. However, + * SAX does not support comment modes, so they will be silently dropped. + * If they are important to you, call {@link #toDOM} and use {@link + * net.sf.practicalxml.OutputUtil#indentedString} to generate output. */ public String toString(int indentSize) { - return OutputUtil.indentedString(toDOM(), indentSize); + return OutputUtil.indentedString(new SerializationHelper(), indentSize); } @@ -88,10 +147,16 @@ * <p> * This is the best choice for writing XML that will be read by another * party. + * <p> + * <em>Warning:</em> + * This method uses a SAX transformer, to minimize footprint. However, + * SAX does not support comment modes, so they will be silently dropped. + * If they are important to you, call {@link #toDOM} and use {@link + * net.sf.practicalxml.OutputUtil#compactStream} to generate output. */ public void toStream(OutputStream out) { - OutputUtil.compactStream(toDOM(), out); + OutputUtil.compactStream(new SerializationHelper(), out); } @@ -99,6 +164,12 @@ * Writes the tree rooted at this element to an <code>OutputStream</code>, * using a specified encoding, without a prologue or whitepspace between * nodes. + * <p> + * <em>Warning:</em> + * This method uses a SAX transformer, to minimize footprint. However, + * SAX does not support comment modes, so they will be silently dropped. + * If they are important to you, call {@link #toDOM} and use {@link + * net.sf.practicalxml.OutputUtil#compactStream} to generate output. */ public void toStream(OutputStream out, String encoding) { @@ -106,27 +177,51 @@ } - /** - * Generates a new DOM document, with this element as the root. - */ - public Document toDOM() +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + @Override + protected void appendToElement(Element parent) { - Element root = DomUtil.newDocument(_nsUri, _qname); + appendChildren(DomUtil.appendChild(parent, _nsUri, _qname)); + } + + + private void appendChildren(Element elem) + { + for (Node child : _attribs) + { + child.appendToElement(elem); + } for (Node child : _children) { - child.appendToElement(root); + child.appendToElement(elem); } - return root.getOwnerDocument(); } - @Override - protected void appendToElement(Element parent) + private Attributes getAttributes() { - Element elem = DomUtil.appendChild(parent, _nsUri, _qname); - for (Node child : _children) + AttributesImpl result = new AttributesImpl(); + for (AttributeNode attr : _attribs) { - child.appendToElement(elem); + attr.appendToAttributes(result); } + return result; } + + + private class SerializationHelper + extends XMLFilterImpl + { + @Override + public void parse(InputSource input) + throws SAXException, IOException + { + startDocument(); + toSAX(getContentHandler()); + endDocument(); + } + } } Modified: trunk/src/main/java/net/sf/practicalxml/builder/Node.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/Node.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/builder/Node.java 2009-04-27 12:41:46 UTC (rev 79) @@ -15,6 +15,8 @@ package net.sf.practicalxml.builder; import org.w3c.dom.Element; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; /** @@ -23,15 +25,40 @@ * are immutable, and require their parent to provide context (ie, no back- * pointers). * <p> - * <code>Node</code> is defined as an abstract class because all methods are - * internal. Only <code>ElementNode</code> should define public methods. + * <code>Node</code> is defined as an abstract class (rather than an interface) + * to allow declaration of protected methods and to provide helper methods. */ public abstract class Node implements java.io.Serializable { /** * This method is called internally by {@link ElementNode}, to append - * its children. + * its children to the DOM subtree rooted at the specified element. */ protected abstract void appendToElement(Element elem); + + + /** + * Invokes the passed <code>ContentHandler</code> for this element + * and its children. Default implementation does nothing (appropriate + * for attributes only). + */ + protected void toSAX(ContentHandler handler) + throws SAXException + { + // nothing happening here ... but almost everyone should override + } + + + /** + * Utility method to return a local name from either a qualified or + * non-qualified name. + */ + protected static String getLocalName(String qname) + { + int sepIdx = qname.indexOf(':'); + return (sepIdx < 0) + ? qname + : qname.substring(sepIdx + 1); + } } Added: trunk/src/main/java/net/sf/practicalxml/builder/PINode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/PINode.java (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/builder/PINode.java 2009-04-27 12:41:46 UTC (rev 79) @@ -0,0 +1,54 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; + +import org.w3c.dom.Element; +import org.w3c.dom.ProcessingInstruction; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; + + +/** + * Holds a processing instruction. + */ +public class PINode extends Node +{ + private static final long serialVersionUID = 1L; + + private String _target; + private String _data; + + public PINode(String target, String data) + { + _target = target; + _data = data; + } + + + @Override + protected void appendToElement(Element parent) + { + ProcessingInstruction pi = parent.getOwnerDocument() + .createProcessingInstruction(_target, _data); + parent.appendChild(pi); + } + + + @Override + protected void toSAX(ContentHandler handler) throws SAXException + { + handler.processingInstruction(_target, _data); + } +} Modified: trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java 2009-04-27 12:41:46 UTC (rev 79) @@ -15,10 +15,16 @@ package net.sf.practicalxml.builder; import org.w3c.dom.Element; +import org.xml.sax.ContentHandler; +import org.xml.sax.SAXException; import net.sf.practicalxml.DomUtil; +/** + * Holds element content. Will be converted to DOM as <code>Text</code> + * (not <code>CDATASection</code>). + */ public class TextNode extends Node { private static final long serialVersionUID = 1L; @@ -36,4 +42,11 @@ { DomUtil.appendText(parent, _content); } + + + @Override + protected void toSAX(ContentHandler handler) throws SAXException + { + handler.characters(_content.toCharArray(), 0, _content.length()); + } } Modified: trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java 2009-04-27 12:41:46 UTC (rev 79) @@ -96,9 +96,22 @@ /** * Creates a comment node. + * <p> + * <em>Warning</em>: + * Comment nodes are not reported by SAX sources. If comments are + * important to you, convert to DOM before serialization. */ public static Node comment(String text) { return new CommentNode(text); } + + + /** + * Creates a processing instruction node. + */ + public static Node processingInstruction(String target, String data) + { + return new PINode(target, data); + } } Modified: trunk/src/main/java/net/sf/practicalxml/builder/package.html =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/package.html 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/main/java/net/sf/practicalxml/builder/package.html 2009-04-27 12:41:46 UTC (rev 79) @@ -1,5 +1,31 @@ <html> <body> - This package contains a tool for declarative creation of XML documents. + <code>XmlBuilder</code> is a tool for declarative construction of XML. + It was originally created to generate XML for unit tests, but is useful + wherever you want to construct XML documents with minimal code and a + low memory footprint. + <p> + The two classes of interest are {@link net.sf.practicalxml.builder.ElementNode} + and {@link net.sf.practicalxml.builder.XmlBuilder}: the former contains public + methods for transforming a tree into various representations, while the latter + contains static methods for building such a tree: + + <pre> + import static net.sf.practicalxml.builder.XmlBuilder.*; + + // ... + + ElementNode root = + element("root", + element("child1", + text("this is some <text>")), + element("child2", + attribute("foo", "bar"), + attribute("baz", "biff")), + element("http::www.example.com/foo", "ns:child3")); + + Document dom = root.toDOM(); + String out = root.toString(); + </pre> </body> </html> \ No newline at end of file Modified: trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java 2009-04-27 12:41:46 UTC (rev 79) @@ -15,7 +15,13 @@ package net.sf.practicalxml; import java.io.ByteArrayOutputStream; +import java.io.IOException; + import org.w3c.dom.Element; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.XMLReader; +import org.xml.sax.helpers.XMLFilterImpl; public class TestOutputUtil @@ -44,6 +50,33 @@ public final static String SOME_TEXT = "blah"; + /** + * An XMLReader that emits a specified series of nested tags. + */ + private static class MyXMLReader + extends XMLFilterImpl + { + private String[] _elems; + + public MyXMLReader(String... elems) + { + _elems = elems; + } + + @Override + public void parse(InputSource input) + throws SAXException, IOException + { + getContentHandler().startDocument(); + for (int ii = 0 ; ii < _elems.length ; ii++) + getContentHandler().startElement(null, _elems[ii], _elems[ii], null); + for (int ii = _elems.length -1 ; ii >= 0 ; ii--) + getContentHandler().endElement(null, _elems[ii], _elems[ii]); + getContentHandler().endDocument(); + } + } + + //---------------------------------------------------------------------------- // Test Cases -- in most of these tests we look for overall structure, assume // that the output transform will do the right thing @@ -69,7 +102,7 @@ } - public void testCompactStringSingleElement() throws Exception + public void testCompactStringSingleElementDOM() throws Exception { Element root = DomUtil.newDocument(EL_ROOT); @@ -78,7 +111,7 @@ } - public void testCompactStringParentChild() throws Exception + public void testCompactStringParentChildDOM() throws Exception { Element root = DomUtil.newDocument(EL_ROOT); DomUtil.appendChild(root, EL_CHILD); @@ -88,6 +121,22 @@ } + public void testCompactStringSingleElementSAX() throws Exception + { + XMLReader reader = new MyXMLReader(EL_ROOT); + String s = OutputUtil.compactString(reader); + assertEquals(EL_ROOT_SOLO, s); + } + + + public void testCompactStringParentChildSAX() throws Exception + { + XMLReader reader = new MyXMLReader(EL_ROOT, EL_CHILD); + String s = OutputUtil.compactString(reader); + assertEquals(EL_ROOT_START + EL_CHILD_SOLO + EL_ROOT_END, s); + } + + public void testCompactStringWithText() throws Exception { Element root = DomUtil.newDocument(EL_ROOT); @@ -98,6 +147,29 @@ } + public void testIndentedStringParentChildDOM() throws Exception + { + Element root = DomUtil.newDocument(EL_ROOT); + Element child = DomUtil.appendChild(root, EL_CHILD); + DomUtil.setText(child, SOME_TEXT); + + String s = OutputUtil.indentedString(root.getOwnerDocument(), 4); + assertMultiline(EL_ROOT_START + + "\n " + EL_CHILD_START + SOME_TEXT + EL_CHILD_END + + "\n" + EL_ROOT_END + "\n", s); + } + + + public void testIndentedStringParentChildSAX() throws Exception + { + XMLReader reader = new MyXMLReader(EL_ROOT, EL_CHILD); + String s = OutputUtil.indentedString(reader, 4); + assertMultiline(EL_ROOT_START + + "\n " + EL_CHILD_SOLO + + "\n" + EL_ROOT_END + "\n", s); + } + + public void testIndentedStringParentChildText() throws Exception { Element root = DomUtil.newDocument(EL_ROOT); @@ -111,7 +183,7 @@ } - public void testCompactStreamAsciiContent() throws Exception + public void testCompactStreamAsciiContentDOM() throws Exception { Element root = DomUtil.newDocument("foo"); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -128,6 +200,23 @@ } + public void testCompactStreamAsciiContentSAX() throws Exception + { + XMLReader reader = new MyXMLReader("foo"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + OutputUtil.compactStream(reader, out); + byte[] data = out.toByteArray(); + assertEquals(6, data.length); + assertEquals('<', data[0]); + assertEquals('f', data[1]); + assertEquals('o', data[2]); + assertEquals('o', data[3]); + assertEquals('/', data[4]); + assertEquals('>', data[5]); + } + + public void testCompactStreamNonAsciiDefaultEncoding() throws Exception { Element root = DomUtil.newDocument("\u00C0\u00C1"); @@ -147,7 +236,7 @@ } - public void testCompactStreamNonAsciiISO8859Encoding() throws Exception + public void testCompactStreamNonAsciiISO8859EncodingDOM() throws Exception { Element root = DomUtil.newDocument("\u00C0\u00C1"); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -169,6 +258,30 @@ assertTrue("no encoding", s.indexOf("encoding") > 0); assertTrue("incorrect encoding", s.indexOf("iso-8859-1") > 0 || s.indexOf("ISO-8859-1") > 0); + } + + public void testCompactStreamNonAsciiISO8859EncodingSAX() throws Exception + { + XMLReader reader = new MyXMLReader("\u00C0\u00C1"); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + + OutputUtil.compactStream(reader, out, "ISO-8859-1"); + byte[] data = out.toByteArray(); + + // look for specific bytes for the element ... note reverse order + int idx = data.length - 1; + assertEquals('>', data[idx--]); + assertEquals('/', data[idx--]); + assertEquals(0xC1, data[idx--] & 0xFF); + assertEquals(0xC0, data[idx--] & 0xFF); + assertEquals('<', data[idx]); + + // convert to string to check for prologue + String s = new String(data, "ISO-8859-1"); + assertTrue("no prologue", s.startsWith("<?xml")); + assertTrue("no encoding", s.indexOf("encoding") > 0); + assertTrue("incorrect encoding", + s.indexOf("iso-8859-1") > 0 || s.indexOf("ISO-8859-1") > 0); } } Modified: trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java 2009-04-25 13:59:44 UTC (rev 78) +++ trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java 2009-04-27 12:41:46 UTC (rev 79) @@ -15,6 +15,12 @@ package net.sf.practicalxml.builder; import java.io.ByteArrayOutputStream; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; @@ -23,6 +29,9 @@ import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; +import org.w3c.dom.ProcessingInstruction; +import org.xml.sax.Attributes; +import org.xml.sax.ContentHandler; import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; @@ -55,6 +64,71 @@ } + private static class MockContentHandler + implements InvocationHandler + { + public ContentHandler getHandler() + throws Exception + { + return (ContentHandler)Proxy.newProxyInstance( + ContentHandler.class.getClassLoader(), + new Class[] { ContentHandler.class }, + this); + } + + private ArrayList<String> _names = new ArrayList<String>(); + private ArrayList<Object[]> _args = new ArrayList<Object[]>(); + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable + { + // this is a hack for characters + for (int ii = 0 ; ii < args.length ; ii++) + { + if (args[ii] instanceof char[]) + args[ii] = new String((char[])args[ii]); + } + + _names.add(method.getName()); + _args.add(args); + return null; + } + + /** + * Asserts that the specific sequence of methods was called on this + * handler. + */ + public void assertInvocationSequence(String... methodNames) + { + List<String> expected = Arrays.asList(methodNames); + assertEquals(expected, _names); + } + + /** + * Asserts the name and argument values for a specific call to this + * handler. For convenience, ignores any arguments past the expected + * list. + */ + public void assertInvocation(int callNum, String methodName, Object... args) + { + assertEquals(methodName, _names.get(callNum)); + for (int ii = 0 ; ii < args.length ; ii++) + { + assertEquals("argument " + ii, args[ii], _args.get(callNum)[ii]); + } + } + + /** + * Returns a specific argument passed to a specific invocation. This + * allows the caller to make test-specific assertions. + */ + public Object getInvocationArgument(int callNum, int argNum) + { + return _args.get(callNum)[argNum]; + } + } + + //---------------------------------------------------------------------------- // Test Cases //---------------------------------------------------------------------------- @@ -62,114 +136,212 @@ public void testSingleElement() throws Exception { ElementNode node = element("foo"); - assertEquals("<foo/>", node.toString()); Document dom = node.toDOM(); assertRootElement(dom, null, "foo", 0); + + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", "endElement"); + handler.assertInvocation(0, "startElement", null, "foo", "foo"); + + assertEquals("<foo/>", node.toString()); } - public void testNamespacedSingleElement() throws Exception + public void testSingleElementDefaultNamespace() throws Exception { ElementNode node = element("foo", "bar"); + + Document dom = node.toDOM(); + assertRootElement(dom, "foo", "bar", 0); + + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", "endElement"); + handler.assertInvocation(0, "startElement", "foo", "bar", "bar"); + assertEquals("<bar xmlns=\"foo\"/>", node.toString()); + } + + public void testSingleElementQualifiedNamespace() throws Exception + { + ElementNode node = element("foo", "bar:baz"); + Document dom = node.toDOM(); - assertRootElement(dom, "foo", "bar", 0); } + assertRootElement(dom, "foo", "bar:baz", 0); + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", "endElement"); + handler.assertInvocation(0, "startElement", "foo", "baz", "bar:baz"); + assertEquals("<bar:baz xmlns:bar=\"foo\"/>", node.toString()); + } + + public void testNestedElement() throws Exception { ElementNode node = element("foo", element("bar")); - assertEquals("<foo><bar/></foo>", node.toString()); Document dom = node.toDOM(); assertRootElement(dom, null, "foo", 1); assertElement(dom, "/foo/bar", null, "bar", 0); + + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", + "startElement", "endElement", + "endElement"); + handler.assertInvocation(0, "startElement", null, "foo", "foo"); + handler.assertInvocation(1, "startElement", null, "bar", "bar"); + + assertEquals("<foo><bar/></foo>", node.toString()); } public void testAttribute() throws Exception { - Document dom = element("foo", + ElementNode node = element("foo", attribute("argle", "bargle", "wargle"), - attribute("bar", "baz")) - .toDOM(); + attribute("bar", "baz")); + Document dom = node.toDOM(); Element root = dom.getDocumentElement(); assertEquals("wargle", root.getAttributeNS("argle", "bargle")); assertEquals("baz", root.getAttribute("bar")); + + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", "endElement"); + handler.assertInvocation(0, "startElement", null, "foo", "foo"); + + Attributes attrs = (Attributes)handler.getInvocationArgument(0, 3); + assertEquals(2, attrs.getLength()); } public void testTextElement() throws Exception { - Document dom = element("foo", text("bar")) - .toDOM(); + ElementNode node = element("foo", text("bar")); + Document dom = node.toDOM(); Element root = dom.getDocumentElement(); assertEquals("bar", DomUtil.getText(root)); + + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", + "characters", + "endElement"); + handler.assertInvocation(0, "startElement", null, "foo", "foo"); + handler.assertInvocation(1, "characters", "bar", 0, 3); + + assertEquals("<foo>bar</foo>", node.toString()); } public void testConsecutiveTextElements() throws Exception { - Document dom = element("foo", text("bar"), text("baz")) - .toDOM(); + ElementNode node = element("foo", text("bar"), text("baz")); + Document dom = node.toDOM(); Element root = dom.getDocumentElement(); assertEquals("barbaz", DomUtil.getText(root)); + + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", + "characters", "characters", + "endElement"); + handler.assertInvocation(0, "startElement", null, "foo", "foo"); + handler.assertInvocation(1, "characters", "bar", 0, 3); + handler.assertInvocation(2, "characters", "baz", 0, 3); + + assertEquals("<foo>barbaz</foo>", node.toString()); } public void testComment() throws Exception { - Document dom = element("foo", comment("bar")) - .toDOM(); + ElementNode node = element("foo", comment("bar")); + Document dom = node.toDOM(); Element root = dom.getDocumentElement(); assertEquals(1, root.getChildNodes().getLength()); - Comment child = (Comment)root.getChildNodes().item(0); assertEquals("bar", child.getNodeValue()); + + // note: ContentHandler knows nothing of comments + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", "endElement"); + handler.assertInvocation(0, "startElement", null, "foo", "foo"); + + +// assertEquals("<foo><!--bar--></foo>", node.toString()); } + public void testPI() throws Exception + { + ElementNode node = element("foo", processingInstruction("argle", "bargle")); + + Document dom = node.toDOM(); + Element root = dom.getDocumentElement(); + assertEquals(1, root.getChildNodes().getLength()); + ProcessingInstruction child = (ProcessingInstruction)root.getChildNodes().item(0); + assertEquals("argle", child.getTarget()); + assertEquals("bargle", child.getData()); + + MockContentHandler handler = new MockContentHandler(); + node.toSAX(handler.getHandler()); + handler.assertInvocationSequence( + "startElement", + "processingInstruction", + "endElement"); + handler.assertInvocation(1, "processingInstruction", "argle", "bargle"); + + assertEquals("<foo><?argle bargle?></foo>", node.toString()); + } + + public void testAddChild() throws Exception { ElementNode root = element("foo"); assertSame(root, root.addChild(element("bar"))); - Element eRoot = root.toDOM().getDocumentElement(); - assertEquals("foo", eRoot.getNodeName()); - assertEquals(1, eRoot.getChildNodes().getLength()); - assertEquals("bar", eRoot.getChildNodes().item(0).getNodeName()); - } + Document dom = root.toDOM(); + assertRootElement(dom, null, "foo", 1); + MockContentHandler handler = new MockContentHandler(); + root.toSAX(handler.getHandler()); + handler.assertInvocationSequence("startElement", + "startElement", "endElement", + "endElement"); + handler.assertInvocation(0, "startElement", null, "foo", "foo"); + handler.assertInvocation(1, "startElement", null, "bar", "bar"); - public void testToString() throws Exception - { - String s = element("foo", - attribute("argle", "bar&gle"), - element("bar", text("baz"))) - .toString(); - assertEquals("<foo argle=\"bar&gle\"><bar>baz</bar></foo>", s); + assertEquals("<foo><bar/></foo>", root.toString()); } public void testToStringIndented() throws Exception { - String s = element("foo", element("bar", text("baz"))) - .toString(3); + ElementNode root = element("foo", element("bar", text("baz"))); + + String s = root.toString(3); assertMultiline("<foo>\n <bar>baz</bar>\n</foo>\n", s); } public void testToStream() throws Exception { + ElementNode root = element("foo", element("b\u00e2r", text("baz"))); + ByteArrayOutputStream out = new ByteArrayOutputStream(); - element("foo", element("b\u00e2r", text("baz"))) - .toStream(out); + root.toStream(out); String s = new String(out.toByteArray(), "UTF-8"); assertEquals("<foo><b\u00e2r>baz</b\u00e2r></foo>", s); @@ -178,9 +350,10 @@ public void testToStreamWithPrologue() throws Exception { + ElementNode root = element("f\u00f6o", element("bar", text("baz"))); + ByteArrayOutputStream out = new ByteArrayOutputStream(); - element("f\u00f6o", element("bar", text("baz"))) - .toStream(out, "ISO-8859-1"); + root.toStream(out, "ISO-8859-1"); byte[] b = out.toByteArray(); assertEquals(0xF6, b[b.length-3] & 0xFF); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-04-25 13:59:46
|
Revision: 78 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=78&view=rev Author: kdgregory Date: 2009-04-25 13:59:44 +0000 (Sat, 25 Apr 2009) Log Message: ----------- refactor tests Modified Paths: -------------- trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java Modified: trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java 2009-04-25 13:17:38 UTC (rev 77) +++ trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java 2009-04-25 13:59:44 UTC (rev 78) @@ -16,6 +16,10 @@ import java.io.ByteArrayOutputStream; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathFactory; + import org.w3c.dom.Comment; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -28,44 +32,60 @@ public class TestXmlBuilder extends AbstractTestCase { +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + private void assertRootElement(Document dom, String nsUri, String name, int childCount) + throws Exception + { + assertElement(dom, "/*[1]", nsUri, name, childCount); + } + + + private void assertElement(Document dom, String path, String nsUri, String name, int childCount) + throws Exception + { + XPath xpath = XPathFactory.newInstance().newXPath(); + Element elem = (Element)xpath.evaluate(path, dom, XPathConstants.NODE); + assertNotNull(elem); + assertEquals(nsUri, elem.getNamespaceURI()); + assertEquals(name, elem.getNodeName()); + assertEquals(childCount, elem.getChildNodes().getLength()); + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + public void testSingleElement() throws Exception { - Document dom = element("foo") - .toDOM(); + ElementNode node = element("foo"); + assertEquals("<foo/>", node.toString()); - Element root = dom.getDocumentElement(); - assertNull(root.getNamespaceURI()); - assertEquals("foo", root.getNodeName()); - assertEquals(0, root.getChildNodes().getLength()); + Document dom = node.toDOM(); + assertRootElement(dom, null, "foo", 0); } public void testNamespacedSingleElement() throws Exception { - Document dom = element("foo", "bar") - .toDOM(); + ElementNode node = element("foo", "bar"); + assertEquals("<bar xmlns=\"foo\"/>", node.toString()); - Element root = dom.getDocumentElement(); - assertEquals("foo", root.getNamespaceURI()); - assertEquals("bar", root.getNodeName()); - assertEquals(0, root.getChildNodes().getLength()); - } + Document dom = node.toDOM(); + assertRootElement(dom, "foo", "bar", 0); } public void testNestedElement() throws Exception { - Document dom = element("foo", element("bar")) - .toDOM(); + ElementNode node = element("foo", element("bar")); + assertEquals("<foo><bar/></foo>", node.toString()); - Element root = dom.getDocumentElement(); - assertNull(root.getNamespaceURI()); - assertEquals("foo", root.getNodeName()); - assertEquals(1, root.getChildNodes().getLength()); - - Element child = (Element)root.getChildNodes().item(0); - assertNull(child.getNamespaceURI()); - assertEquals("bar", child.getNodeName()); - assertEquals(0, child.getChildNodes().getLength()); + Document dom = node.toDOM(); + assertRootElement(dom, null, "foo", 1); + assertElement(dom, "/foo/bar", null, "bar", 0); } @@ -129,9 +149,11 @@ public void testToString() throws Exception { - String s = element("foo", element("bar", text("baz"))) + String s = element("foo", + attribute("argle", "bar&gle"), + element("bar", text("baz"))) .toString(); - assertEquals("<foo><bar>baz</bar></foo>", s); + assertEquals("<foo argle=\"bar&gle\"><bar>baz</bar></foo>", s); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-04-25 13:17:39
|
Revision: 77 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=77&view=rev Author: kdgregory Date: 2009-04-25 13:17:38 +0000 (Sat, 25 Apr 2009) Log Message: ----------- Add "internal" package Remove dependency on Jakarta Commons lang Modified Paths: -------------- trunk/pom.xml trunk/src/main/java/net/sf/practicalxml/DomUtil.java trunk/src/main/java/net/sf/practicalxml/XmlUtil.java Added Paths: ----------- trunk/src/main/java/net/sf/practicalxml/internal/ trunk/src/main/java/net/sf/practicalxml/internal/StringUtils.java trunk/src/main/java/net/sf/practicalxml/internal/package.html trunk/src/test/java/net/sf/practicalxml/internal/ trunk/src/test/java/net/sf/practicalxml/internal/TestStringUtils.java Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2009-04-25 12:24:37 UTC (rev 76) +++ trunk/pom.xml 2009-04-25 13:17:38 UTC (rev 77) @@ -122,11 +122,6 @@ <dependencies> <dependency> - <groupId>commons-lang</groupId> - <artifactId>commons-lang</artifactId> - <version>2.3</version> - </dependency> - <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.2</version> Modified: trunk/src/main/java/net/sf/practicalxml/DomUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2009-04-25 12:24:37 UTC (rev 76) +++ trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2009-04-25 13:17:38 UTC (rev 77) @@ -27,11 +27,10 @@ import org.w3c.dom.NodeList; import org.w3c.dom.Text; +import net.sf.practicalxml.internal.StringUtils; import net.sf.practicalxml.xpath.NamespaceResolver; -import org.apache.commons.lang.StringUtils; - /** * A collection of static utility methods for working with DOM trees. * Most of these are usability workarounds for the <code>org.w3c.dom</code> Modified: trunk/src/main/java/net/sf/practicalxml/XmlUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-04-25 12:24:37 UTC (rev 76) +++ trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-04-25 13:17:38 UTC (rev 77) @@ -21,7 +21,9 @@ import java.util.GregorianCalendar; import java.util.TimeZone; +import net.sf.practicalxml.internal.StringUtils; + /** * A collection of static methods for manipulating XML as text. */ @@ -399,11 +401,9 @@ // caller has checked &#, so skip them curPos += 2; - boolean isHex = false; int multiplier = 10; if (s.charAt(curPos) == 'x') { - isHex = true; multiplier = 16; curPos++; } @@ -415,7 +415,7 @@ char c = s.charAt(curPos + ii); if (c == ';') break; - int cVal = convertDigit(c, isHex); + int cVal = StringUtils.parseDigit(c, multiplier); if (cVal < 0) return '\0'; value = value * multiplier + cVal; @@ -426,22 +426,4 @@ return (char)value; } - - - // FIXME - refactor this into a common method - /** - * Verifies that the passed character is a digit, and converts it to its - * numeric value if yes. Returns -1 if not a legal digit. - */ - private static int convertDigit(char c, boolean allowHex) - { - if ((c >= '0') && (c <= '9')) - return c - '0'; - if (allowHex && (c >= 'a') && (c <= 'f')) - return c - 'a' + 10; - if (allowHex && (c >= 'A') && (c <= 'F')) - return c - 'A' + 10; - return -1; - } - } Added: trunk/src/main/java/net/sf/practicalxml/internal/StringUtils.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/internal/StringUtils.java (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/internal/StringUtils.java 2009-04-25 13:17:38 UTC (rev 77) @@ -0,0 +1,108 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.internal; + + +/** + * Static methods for working with strings and characters. This class exists + * primarily to break dependency on Jakarta Commons. + */ +public class StringUtils +{ + /** + * Returns true if the passed string is null or zero-length; false + * otherwise (including a string containing only whitespace). This + * is a replacement for the Jakarta Commons method with the same + * name. + */ + public static boolean isEmpty(String s) + { + return (s == null) || (s.length() == 0); + } + + + /** + * Returns true if the passed string is null, zero-length, or contains + * only whitespace characters as defined by Character.isWhitespace(); + * false otherwise. This is a replacement for the Jakarta Commons method + * with the same name. + */ + public static boolean isBlank(String s) + { + if ((s == null) || (s.length() == 0)) + return true; + + for (int ii = 0 ; ii < s.length() ; ii++) + { + if (!Character.isWhitespace(s.charAt(ii))) + return false; + } + + return true; + } + + + /** + * Trims all whitespace characters (as defined by Character.isWhitespace()) + * from both ends of the string, returning an empty string if there's + * nothing left. Will also return an empty string if passed null. This is a + * replacement for the Jakarta Commons method with the same name. + */ + public static String trimToEmpty(String s) + { + if ((s == null) || (s.length() == 0)) + return ""; + + int i0 = 0; + int i1 = s.length() - 1; + while (i0 <= i1) + { + if (Character.isWhitespace(s.charAt(i0))) + i0++; + else if (Character.isWhitespace(s.charAt(i1))) + i1--; + else + return s.substring(i0, i1 + 1); + } + + return ""; + } + + + /** + * Parses the passed character as a digit in the specified base, + * returning its value. Bases > 10 are represented by ASCII letters + * in the range A to Z (or a to z). Base 36 is the largest supported. + * + * @return The value, or -1 if the character is not a valid digit + * in the specified base (this method will typically be used + * in a loop, so no good reason to force exception checking). + */ + public static int parseDigit(char c, int base) + { + int value = -1; + if ((c >= '0') && (c <= '9')) + value = c - '0'; + else if ((c >= 'a') && (c <= 'z')) + value = c - 'a' + 10; + else if ((c >= 'A') && (c <= 'Z')) + value = c - 'A' + 10; + + if (value >= base) + value = -1; + return value; + } + +} Added: trunk/src/main/java/net/sf/practicalxml/internal/package.html =================================================================== --- trunk/src/main/java/net/sf/practicalxml/internal/package.html (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/internal/package.html 2009-04-25 13:17:38 UTC (rev 77) @@ -0,0 +1,4 @@ +This package contains classes used internally by the PracticalXML library. +Applications should not rely on the API of these classes, or indeed of their +continued existence. In most cases, there's a better alternative in a third- +party library. \ No newline at end of file Added: trunk/src/test/java/net/sf/practicalxml/internal/TestStringUtils.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/internal/TestStringUtils.java (rev 0) +++ trunk/src/test/java/net/sf/practicalxml/internal/TestStringUtils.java 2009-04-25 13:17:38 UTC (rev 77) @@ -0,0 +1,77 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.internal; + +import junit.framework.TestCase; + + +public class TestStringUtils extends TestCase +{ + public void testIsEmpty() throws Exception + { + assertTrue(StringUtils.isEmpty(null)); + assertTrue(StringUtils.isEmpty("")); + + assertFalse(StringUtils.isEmpty("A")); + assertFalse(StringUtils.isEmpty(" ")); + } + + + public void testIsBlank() throws Exception + { + assertTrue(StringUtils.isBlank(null)); + assertTrue(StringUtils.isBlank("")); + assertTrue(StringUtils.isBlank(" ")); + assertTrue(StringUtils.isBlank(" \n ")); + + assertFalse(StringUtils.isBlank("A")); + assertFalse(StringUtils.isBlank(" A ")); + assertFalse(StringUtils.isBlank("\u00A0")); + } + + + public void testTrimToEmpty() throws Exception + { + assertEquals("", StringUtils.trimToEmpty(null)); + assertEquals("", StringUtils.trimToEmpty("")); + assertEquals("", StringUtils.trimToEmpty(" \n \t ")); + + assertEquals("A", StringUtils.trimToEmpty(" A\n ")); + assertEquals("AB", StringUtils.trimToEmpty(" AB\n ")); + assertEquals("\u00A0", StringUtils.trimToEmpty("\u00A0")); + } + + + public void testParseDigit() throws Exception + { + assertEquals(0, StringUtils.parseDigit('0', 10)); + assertEquals(9, StringUtils.parseDigit('9', 10)); + assertEquals(-1, StringUtils.parseDigit('A', 10)); + + assertEquals(0, StringUtils.parseDigit('0', 16)); + assertEquals(9, StringUtils.parseDigit('9', 16)); + assertEquals(10, StringUtils.parseDigit('A', 16)); + assertEquals(15, StringUtils.parseDigit('F', 16)); + assertEquals(-1, StringUtils.parseDigit('G', 16)); + assertEquals(10, StringUtils.parseDigit('a', 16)); + assertEquals(15, StringUtils.parseDigit('f', 16)); + assertEquals(-1, StringUtils.parseDigit('g', 16)); + + assertEquals(35, StringUtils.parseDigit('Z', 36)); + assertEquals(35, StringUtils.parseDigit('z', 36)); + + assertEquals(-1, StringUtils.parseDigit('!', 100)); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-04-25 12:24:50
|
Revision: 76 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=76&view=rev Author: kdgregory Date: 2009-04-25 12:24:37 +0000 (Sat, 25 Apr 2009) Log Message: ----------- Add XmlUtil.escape, XmlUtil.unescape Modified Paths: -------------- trunk/pom.xml trunk/src/main/java/net/sf/practicalxml/XmlUtil.java trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2009-01-14 14:14:44 UTC (rev 75) +++ trunk/pom.xml 2009-04-25 12:24:37 UTC (rev 76) @@ -5,7 +5,7 @@ <groupId>net.sf.practicalxml</groupId> <artifactId>practicalxml</artifactId> <packaging>jar</packaging> - <version>1.0.0</version> + <version>1.0.1</version> <name>practicalxml</name> <url>http://sourceforge.net/projects/practicalxml/</url> Modified: trunk/src/main/java/net/sf/practicalxml/XmlUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-01-14 14:14:44 UTC (rev 75) +++ trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-04-25 12:24:37 UTC (rev 76) @@ -122,6 +122,95 @@ } + /** + * Escapes the passed string, converting the five reserved XML characters + * into their entities: &amp;, &lt;, &gt;, &apos;, and + * &quot;. If the string does not contain any of these characters, it + * will be returned unchanged. If passed <code>null</code>, returns an + * empty string. + * <p> + * Yes, this method is available elsewhere, eg Jakarta Commons. I'm trying + * to minimize external dependencies from this library, so am reinventing + * a few small wheels (but they're round!). + */ + public static String escape(String s) + { + if (s == null) + return ""; + + StringBuilder buf = new StringBuilder(s.length()); + boolean wasEscaped = false; + + for (int ii = 0 ; ii < s.length() ; ii++) + { + char c = s.charAt(ii); + switch (c) + { + case '&' : + buf.append("&"); + wasEscaped = true; + break; + case '<' : + buf.append("<"); + wasEscaped = true; + break; + case '>' : + buf.append(">"); + wasEscaped = true; + break; + case '\'' : + buf.append("'"); + wasEscaped = true; + break; + case '"' : + buf.append("""); + wasEscaped = true; + break; + default : + buf.append(c); + } + } + + return wasEscaped ? buf.toString() : s; + } + + + /** + * Unescapes the passed string, converting the five XML entities + * (&amp;, &lt;, &gt;, &apos;, and &quot;) into + * their correspinding characters. Also converts any numeric entities + * into their characters. If the string does not contain any convertable + * entities, it will be returned unchanged. If passed <code>null</code>, + * returns an empty string. + * <p> + * Yes, this method is available elsewhere, eg Jakarta Commons. + */ + public static String unescape(String s) + { + if (s == null) + return ""; + + StringBuilder buf = new StringBuilder(s.length() + 20); + boolean wasEscaped = false; + + for (int ii = 0 ; ii < s.length() ; ii++) + { + char c = s.charAt(ii); + switch (c) + { + case '&' : + ii = unescapeHelper(s, ii, buf); + wasEscaped = true; + break; + default : + buf.append(c); + } + } + + return wasEscaped ? buf.toString() : s; + } + + //---------------------------------------------------------------------------- // Internals //---------------------------------------------------------------------------- @@ -234,4 +323,125 @@ : "GMT"; cal.setTimeZone(TimeZone.getTimeZone(tz)); } + + + /** + * Attempts to recognize an entity in the passed string, appending the + * corresponding character to the passed buffer. If unable to recognize + * an entity, appends the current character (an ampersand) to the buffer. + * Returns the updated string index (position of the trailing semi-colon). + */ + private static int unescapeHelper(String s, int curPos, StringBuilder buf) + { + // the case of a malformed entity at the end of the string should be + // all but nonexistent in the real world, so rather than clutter the + // code with index tests, I'll just catch the exception + try + { + if (s.startsWith("&", curPos)) + { + buf.append("&"); + return curPos + 4; + } + else if (s.startsWith("'", curPos)) + { + buf.append("'"); + return curPos + 5; + } + else if (s.startsWith(""", curPos)) + { + buf.append('"'); + return curPos + 5; + } + else if (s.startsWith("<", curPos)) + { + buf.append("<"); + return curPos + 3; + } + else if (s.startsWith(">", curPos)) + { + buf.append(">"); + return curPos + 3; + } + else if (s.startsWith("&#", curPos)) + { + char c = numericEntityHelper(s, curPos); + if (c != '\0') + { + buf.append(c); + return s.indexOf(';', curPos); + } + } + } + catch (StringIndexOutOfBoundsException ignored) + { + // fall through to default handler + } + + // it's not an entity that we know how to process, so just copy the + // ampersand and let the rest of the string process + buf.append('&'); + return curPos; + } + + + /** + * Attempts to decode a numeric character entity starting at the current + * position within the string. If able, returns the corresponding character. + * If unable, returns NUL (which is disallowed by both XML 1.0 and XML 1.1). + * <p> + * Limited to + */ + private static char numericEntityHelper(String s, int curPos) + { + int value = 0; + + // caller has checked &#, so skip them + curPos += 2; + + boolean isHex = false; + int multiplier = 10; + if (s.charAt(curPos) == 'x') + { + isHex = true; + multiplier = 16; + curPos++; + } + + // XML is limited to Unicode plane 0, so 4 hex or 5 decimal digits + // ... don't index through entire string looking for semi-colon + for (int ii = 0 ; ii < 6 ; ii++) + { + char c = s.charAt(curPos + ii); + if (c == ';') + break; + int cVal = convertDigit(c, isHex); + if (cVal < 0) + return '\0'; + value = value * multiplier + cVal; + } + + if (value > 65535) + return '\0'; + + return (char)value; + } + + + // FIXME - refactor this into a common method + /** + * Verifies that the passed character is a digit, and converts it to its + * numeric value if yes. Returns -1 if not a legal digit. + */ + private static int convertDigit(char c, boolean allowHex) + { + if ((c >= '0') && (c <= '9')) + return c - '0'; + if (allowHex && (c >= 'a') && (c <= 'f')) + return c - 'a' + 10; + if (allowHex && (c >= 'A') && (c <= 'F')) + return c - 'A' + 10; + return -1; + } + } Modified: trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java 2009-01-14 14:14:44 UTC (rev 75) +++ trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java 2009-04-25 12:24:37 UTC (rev 76) @@ -75,4 +75,60 @@ assertEquals(expected.getTime(), XmlUtil.parseXsdDatetime("2004-10-28T09:10:11.123")); assertEquals(expected.getTime(), XmlUtil.parseXsdDatetime("2004-10-28T04:10:11.123-05:00")); } + + + public void testEscape() throws Exception + { + assertEquals("", XmlUtil.escape(null)); + assertEquals("", XmlUtil.escape("")); + + String s1 = new String("this has nothing to escape"); + assertSame(s1, XmlUtil.escape(s1)); + + assertEquals("this & <string> does "'", + XmlUtil.escape("this & <string> does \"'")); + } + + + public void testUnescape() throws Exception + { + assertEquals("", XmlUtil.unescape(null)); + assertEquals("", XmlUtil.unescape("")); + + String s1 = new String("this has nothing to escape"); + assertSame(s1, XmlUtil.unescape(s1)); + + assertEquals("this string'\"does<&>", + XmlUtil.unescape("this string'"does<&>")); + + assertEquals("this is an &unknown; entity", + XmlUtil.unescape("this is an &unknown; entity")); + } + + + public void testUnescapeWithInvalidNumericEntity() throws Exception + { + assertEquals("𘚟", + XmlUtil.unescape("𘚟")); + assertEquals("𒍅", + XmlUtil.unescape("𒍅")); + assertEquals("cAA;", + XmlUtil.unescape("cAA;")); + + assertEquals("&#;", + XmlUtil.unescape("&#;")); + + assertEquals("&#this is not really an entity", + XmlUtil.unescape("&#this is not really an entity")); + } + + + public void testUnescapeAtEndOfString() throws Exception + { + assertEquals("&", + XmlUtil.unescape("&")); + assertEquals("&am", + XmlUtil.unescape("&am")); + } + } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-01-14 14:14:55
|
Revision: 75 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=75&view=rev Author: kdgregory Date: 2009-01-14 14:14:44 +0000 (Wed, 14 Jan 2009) Log Message: ----------- add license info to POM Modified Paths: -------------- trunk/pom.xml Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2009-01-14 13:44:22 UTC (rev 74) +++ trunk/pom.xml 2009-01-14 14:14:44 UTC (rev 75) @@ -43,6 +43,14 @@ </developer> </developers> + <licenses> + <license> + <name>The Apache Software License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url> + <distribution>repo</distribution> + </license> + </licenses> + <scm> <connection>scm:svn:https://practicalxml.svn.sourceforge.net/svnroot/practicalxml</connection> <developerConnection>scm:svn:https://practicalxml.svn.sourceforge.net/svnroot/practicalxml</developerConnection> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-01-14 13:44:35
|
Revision: 74 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=74&view=rev Author: kdgregory Date: 2009-01-14 13:44:22 +0000 (Wed, 14 Jan 2009) Log Message: ----------- tag release 1.0.0 Added Paths: ----------- tags/rel-1.0/ tags/rel-1.0/pom.xml Removed Paths: ------------- tags/rel-1.0/pom.xml Deleted: tags/rel-1.0/pom.xml =================================================================== --- trunk/pom.xml 2009-01-14 13:37:46 UTC (rev 72) +++ tags/rel-1.0/pom.xml 2009-01-14 13:44:22 UTC (rev 74) @@ -1,127 +0,0 @@ -<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/maven-v4_0_0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <groupId>net.sf.practicalxml</groupId> - <artifactId>practicalxml</artifactId> - <packaging>jar</packaging> - <version>1.0-SNAPSHOT</version> - <name>practicalxml</name> - <url>http://sourceforge.net/projects/practicalxml/</url> - - <description> - A collection of utility classes for working with the XML support built-in - to the JDK. - </description> - - <mailingLists> - <mailingList> - <name>practicalxml-commits</name> - <subscribe>http://lists.sourceforge.net/mailman/listinfo/practicalxml-commits</subscribe> - <unsubscribe>http://lists.sourceforge.net/mailman/listinfo/practicalxml-commits</unsubscribe> - <archive>http://sourceforge.net/mailarchive/forum.php?forum_name=practicalxml-commits</archive> - </mailingList> - </mailingLists> - - <developers> - <developer> - <id>kdgregory</id> - <name>Keith D Gregory</name> - <url>http://www.kdgregory.com</url> - </developer> - <developer> - <id>ebernstein</id> - <name>Eric Bernstein</name> - </developer> - <developer> - <id>kmudrick</id> - <name>Kevin Mudrick</name> - </developer> - <developer> - <id>salewski</id> - <name>Alan D. Salewski</name> - </developer> - </developers> - - <scm> - <connection>scm:svn:https://practicalxml.svn.sourceforge.net/svnroot/practicalxml</connection> - <developerConnection>scm:svn:https://practicalxml.svn.sourceforge.net/svnroot/practicalxml</developerConnection> - <url>http://practicalxml.svn.sourceforge.net/viewvc/practicalxml/</url> - </scm> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <source>1.5</source> - <target>1.5</target> - <compilerArgument>-g</compilerArgument> - </configuration> - </plugin> - </plugins> - </build> - - <reporting> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-project-info-reports-plugin</artifactId> - <version>2.0.1</version> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-javadoc-plugin</artifactId> - <configuration> - <bottom> - <a href="http://sourceforge.net/"> - <img src="http://sflogo.sourceforge.net/sflogo.php?group_id=234884&type=3"> - </a> - </bottom> - </configuration> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-surefire-report-plugin</artifactId> - <version>2.4.2</version> - </plugin> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-changelog-plugin</artifactId> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>cobertura-maven-plugin</artifactId> - <configuration> - <instrumentation> - <excludes> - <exclude>**/Test*.class</exclude> - <exclude>**/AbstractTestCase.class</exclude> - </excludes> - </instrumentation> - <executions> - <execution> - <goals> - <goal>clean</goal> - </goals> - </execution> - </executions> - </configuration> - </plugin> - </plugins> - </reporting> - - <dependencies> - <dependency> - <groupId>commons-lang</groupId> - <artifactId>commons-lang</artifactId> - <version>2.3</version> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>3.8.2</version> - </dependency> - </dependencies> -</project> Copied: tags/rel-1.0/pom.xml (from rev 73, trunk/pom.xml) =================================================================== --- tags/rel-1.0/pom.xml (rev 0) +++ tags/rel-1.0/pom.xml 2009-01-14 13:44:22 UTC (rev 74) @@ -0,0 +1,127 @@ +<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/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>net.sf.practicalxml</groupId> + <artifactId>practicalxml</artifactId> + <packaging>jar</packaging> + <version>1.0.0</version> + <name>practicalxml</name> + <url>http://sourceforge.net/projects/practicalxml/</url> + + <description> + A collection of utility classes for working with the XML support built-in + to the JDK. + </description> + + <mailingLists> + <mailingList> + <name>practicalxml-commits</name> + <subscribe>http://lists.sourceforge.net/mailman/listinfo/practicalxml-commits</subscribe> + <unsubscribe>http://lists.sourceforge.net/mailman/listinfo/practicalxml-commits</unsubscribe> + <archive>http://sourceforge.net/mailarchive/forum.php?forum_name=practicalxml-commits</archive> + </mailingList> + </mailingLists> + + <developers> + <developer> + <id>kdgregory</id> + <name>Keith D Gregory</name> + <url>http://www.kdgregory.com</url> + </developer> + <developer> + <id>ebernstein</id> + <name>Eric Bernstein</name> + </developer> + <developer> + <id>kmudrick</id> + <name>Kevin Mudrick</name> + </developer> + <developer> + <id>salewski</id> + <name>Alan D. Salewski</name> + </developer> + </developers> + + <scm> + <connection>scm:svn:https://practicalxml.svn.sourceforge.net/svnroot/practicalxml</connection> + <developerConnection>scm:svn:https://practicalxml.svn.sourceforge.net/svnroot/practicalxml</developerConnection> + <url>http://practicalxml.svn.sourceforge.net/viewvc/practicalxml/</url> + </scm> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <configuration> + <source>1.5</source> + <target>1.5</target> + <compilerArgument>-g</compilerArgument> + </configuration> + </plugin> + </plugins> + </build> + + <reporting> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>2.0.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <configuration> + <bottom> + <a href="http://sourceforge.net/"> + <img src="http://sflogo.sourceforge.net/sflogo.php?group_id=234884&type=3"> + </a> + </bottom> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-report-plugin</artifactId> + <version>2.4.2</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-changelog-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>cobertura-maven-plugin</artifactId> + <configuration> + <instrumentation> + <excludes> + <exclude>**/Test*.class</exclude> + <exclude>**/AbstractTestCase.class</exclude> + </excludes> + </instrumentation> + <executions> + <execution> + <goals> + <goal>clean</goal> + </goals> + </execution> + </executions> + </configuration> + </plugin> + </plugins> + </reporting> + + <dependencies> + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.3</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>3.8.2</version> + </dependency> + </dependencies> +</project> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-01-14 13:42:14
|
Revision: 73 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=73&view=rev Author: kdgregory Date: 2009-01-14 13:42:04 +0000 (Wed, 14 Jan 2009) Log Message: ----------- update POM for release Modified Paths: -------------- trunk/pom.xml Modified: trunk/pom.xml =================================================================== --- trunk/pom.xml 2009-01-14 13:37:46 UTC (rev 72) +++ trunk/pom.xml 2009-01-14 13:42:04 UTC (rev 73) @@ -5,7 +5,7 @@ <groupId>net.sf.practicalxml</groupId> <artifactId>practicalxml</artifactId> <packaging>jar</packaging> - <version>1.0-SNAPSHOT</version> + <version>1.0.0</version> <name>practicalxml</name> <url>http://sourceforge.net/projects/practicalxml/</url> This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-01-14 13:37:51
|
Revision: 72 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=72&view=rev Author: kdgregory Date: 2009-01-14 13:37:46 +0000 (Wed, 14 Jan 2009) Log Message: ----------- add README Added Paths: ----------- trunk/README.txt Added: trunk/README.txt =================================================================== --- trunk/README.txt (rev 0) +++ trunk/README.txt 2009-01-14 13:37:46 UTC (rev 72) @@ -0,0 +1,48 @@ +Practical XML - Providing What The JDK Doesn't + + This library is designed to be an easy-to-use wrapper around the XML + support built-in to the JDK. It is based on the idea that the JDK + implementations will live forever and be regularly updated, something + that cannot be said about third-party alternatives. + +Licensing and Copyright + + This library is released under the Apache 2 license: + http://www.apache.org/licenses/LICENSE-2.0 + + Copyright is retained by the individual maintainers, for the code + that they have contributed. In the case of patch submissions from + third parties, the patch contributor implicitly assigns copyright + to the maintainer who implements the patch. + +Release Numbering + + All builds post initial release have a three part revision number: + MAJOR.MINOR.PATCH + + MAJOR releases are not required to be backwards compatible with + the previous such release. For example, the 1.0.0 release + has a different package structure than the 0.9.0 release. + It is intended that there will never be a change to the + major release number, although "never" is a long time. + MINOR releases will happen whenever a maintainer decides that + one is needed, typically driven by significant new + functionality and a desire to have the release in a central + repository. Minor releases preserve backwards compatibility: + code written for one minor release will work when linked + with another minor release. + PATCH releases won't actually get released: the PATCH revision + number should be incremented with each checkin, in preference + to using a "-SNAPSHOT" of the next minor release. This will + lock users into keeping their build scripts and local repository + up-to-date, which is a Good Thing. + + Major and minor releases will be tagged in the Subversion repository, + using the form "rel-X.Y", where X and Y are the major and minor release + numbers. Patch releases may be tagged using the form "rel-X.Y.Z", if the + maintainer decides that it's important to rebuild that particular release + (although making it a minor release might be a better idea). + + Major and minor releases will be available for download from Sourceforge + ( http://sourceforge.net/project/showfiles.php?group_id=234884 ), and also + from the Central Maven Repository. This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2009-01-02 13:20:13
|
Revision: 71 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=71&view=rev Author: kdgregory Date: 2009-01-02 13:19:54 +0000 (Fri, 02 Jan 2009) Log Message: ----------- Add license Modified Paths: -------------- trunk/src/example/java/net/sf/practicalxml/example/BuilderExample.java trunk/src/main/java/net/sf/practicalxml/DomUtil.java trunk/src/main/java/net/sf/practicalxml/OutputUtil.java trunk/src/main/java/net/sf/practicalxml/ParseUtil.java trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java trunk/src/main/java/net/sf/practicalxml/XmlException.java trunk/src/main/java/net/sf/practicalxml/XmlUtil.java trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java trunk/src/main/java/net/sf/practicalxml/builder/Node.java trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilderException.java trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java trunk/src/main/java/net/sf/practicalxml/util/ErrorHandlerAdapter.java trunk/src/main/java/net/sf/practicalxml/util/ExceptionErrorHandler.java trunk/src/main/java/net/sf/practicalxml/util/NodeListIterable.java trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java trunk/src/main/java/net/sf/practicalxml/xpath/NamespaceResolver.java trunk/src/main/java/net/sf/practicalxml/xpath/SimpleNamespaceResolver.java trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java trunk/src/main/java/net/sf/practicalxml/xpath/function/Constants.java trunk/src/main/java/net/sf/practicalxml/xpath/function/Lowercase.java trunk/src/main/java/net/sf/practicalxml/xpath/function/Uppercase.java trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java trunk/src/test/java/net/sf/practicalxml/AbstractTestCase.java trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java trunk/src/test/java/net/sf/practicalxml/TestParseUtil.java trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java trunk/src/test/java/net/sf/practicalxml/TestXmlUtil.java trunk/src/test/java/net/sf/practicalxml/builder/TestXmlBuilder.java trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java trunk/src/test/java/net/sf/practicalxml/util/TestNodeListIterable.java trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java trunk/src/test/java/net/sf/practicalxml/xpath/TestFunctionResolver.java trunk/src/test/java/net/sf/practicalxml/xpath/TestNamespaceResolver.java trunk/src/test/java/net/sf/practicalxml/xpath/TestSimpleNamespaceResolver.java trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestXsiBoolean.java Added Paths: ----------- trunk/LICENSE.txt trunk/src/main/resources/ trunk/src/main/resources/LICENSE.txt Added: trunk/LICENSE.txt =================================================================== --- trunk/LICENSE.txt (rev 0) +++ trunk/LICENSE.txt 2009-01-02 13:19:54 UTC (rev 71) @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. Modified: trunk/src/example/java/net/sf/practicalxml/example/BuilderExample.java =================================================================== --- trunk/src/example/java/net/sf/practicalxml/example/BuilderExample.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/example/java/net/sf/practicalxml/example/BuilderExample.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.example; import net.sf.practicalxml.builder.ElementNode; Modified: trunk/src/main/java/net/sf/practicalxml/DomUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import java.util.ArrayList; Modified: trunk/src/main/java/net/sf/practicalxml/OutputUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/OutputUtil.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/OutputUtil.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import java.io.IOException; Modified: trunk/src/main/java/net/sf/practicalxml/ParseUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/ParseUtil.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/ParseUtil.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import java.io.IOException; @@ -8,15 +22,15 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.validation.Schema; -import net.sf.practicalxml.util.ExceptionErrorHandler; - import org.w3c.dom.Document; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import net.sf.practicalxml.util.ExceptionErrorHandler; + /** * A collection of static methods for parsing XML into a DOM, with or without * validation. Modified: trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import java.util.Comparator; Modified: trunk/src/main/java/net/sf/practicalxml/XmlException.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XmlException.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/XmlException.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; Modified: trunk/src/main/java/net/sf/practicalxml/XmlUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/XmlUtil.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import java.text.DateFormat; Modified: trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/builder/AttributeNode.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; import org.w3c.dom.Element; Modified: trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/builder/CommentNode.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; import org.w3c.dom.Comment; Modified: trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/builder/ElementNode.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; import java.io.OutputStream; Modified: trunk/src/main/java/net/sf/practicalxml/builder/Node.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/Node.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/builder/Node.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; import org.w3c.dom.Element; Modified: trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/builder/TextNode.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; import org.w3c.dom.Element; Modified: trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilder.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; Modified: trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilderException.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilderException.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/builder/XmlBuilderException.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.builder; Modified: trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,17 +1,32 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.junit; -import static junit.framework.Assert.*; - import java.util.List; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + import junit.framework.Assert; import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.xpath.XPathWrapper; -import org.w3c.dom.Element; -import org.w3c.dom.Node; +import static junit.framework.Assert.*; + /** * JUnit assertions for DOM documents. These are defined as static methods, * so may be statically imported (although in some cases this will clash Modified: trunk/src/main/java/net/sf/practicalxml/util/ErrorHandlerAdapter.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/util/ErrorHandlerAdapter.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/util/ErrorHandlerAdapter.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.util; import org.xml.sax.ErrorHandler; Modified: trunk/src/main/java/net/sf/practicalxml/util/ExceptionErrorHandler.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/util/ExceptionErrorHandler.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/util/ExceptionErrorHandler.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.util; import java.util.ArrayList; @@ -3,11 +17,11 @@ import java.util.List; -import net.sf.practicalxml.XmlException; - import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; +import net.sf.practicalxml.XmlException; + /** * An error handler that throws <code>XmlException</code> on any error, Modified: trunk/src/main/java/net/sf/practicalxml/util/NodeListIterable.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/util/NodeListIterable.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/util/NodeListIterable.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.util; import java.util.Iterator; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath; import java.util.Collections; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath; import java.util.HashMap; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/NamespaceResolver.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/NamespaceResolver.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/NamespaceResolver.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath; import java.util.ArrayList; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/SimpleNamespaceResolver.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/SimpleNamespaceResolver.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/SimpleNamespaceResolver.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath; import java.util.Arrays; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath; import java.util.HashMap; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/function/Constants.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/function/Constants.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/function/Constants.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath.function; /** Modified: trunk/src/main/java/net/sf/practicalxml/xpath/function/Lowercase.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/function/Lowercase.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/function/Lowercase.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath.function; import org.w3c.dom.Node; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/function/Uppercase.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/function/Uppercase.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/function/Uppercase.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath.function; import org.w3c.dom.Node; Modified: trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml.xpath.function; import org.w3c.dom.Node; @@ -66,4 +80,4 @@ { return Boolean.FALSE; } -} \ No newline at end of file +} Added: trunk/src/main/resources/LICENSE.txt =================================================================== --- trunk/src/main/resources/LICENSE.txt (rev 0) +++ trunk/src/main/resources/LICENSE.txt 2009-01-02 13:19:54 UTC (rev 71) @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed 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. Modified: trunk/src/test/java/net/sf/practicalxml/AbstractTestCase.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/AbstractTestCase.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/test/java/net/sf/practicalxml/AbstractTestCase.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import junit.framework.TestCase; Modified: trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import java.util.List; Modified: trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/test/java/net/sf/practicalxml/TestDomUtilGetPath.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 net.sf.practicalxml; import javax.xml.namespace.NamespaceContext; Modified: trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java 2008-12-30 14:08:10 UTC (rev 70) +++ trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java 2009-01-02 13:19:54 UTC (rev 71) @@ -1,3 +1,17 @@ +// Copyright 2008-2009 severally by the contributors +// +// Licensed 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 ... [truncated message content] |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-30 14:08:12
|
Revision: 70 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=70&view=rev Author: kdgregory Date: 2008-12-30 14:08:10 +0000 (Tue, 30 Dec 2008) Log Message: ----------- XPathWrapper: all forms of evaluate() now take a Node, for consistency with the rest of the world - refactor evaluation Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java Modified: trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java 2008-12-30 14:01:57 UTC (rev 69) +++ trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java 2008-12-30 14:08:10 UTC (rev 70) @@ -1,6 +1,5 @@ package net.sf.practicalxml.xpath; -import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -13,11 +12,10 @@ import javax.xml.xpath.XPathFunction; import javax.xml.xpath.XPathVariableResolver; -import org.w3c.dom.Document; -import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import net.sf.practicalxml.DomUtil; import net.sf.practicalxml.XmlException; @@ -142,7 +140,7 @@ * * @return The wrapper, so that calls may be chained. */ - public XPathWrapper bindFunction(AbstractFunction func) + public XPathWrapper bindFunction(AbstractFunction<?> func) { _functions.addFunction(func); return this; @@ -164,7 +162,7 @@ * * @return The wrapper, so that calls may be chained. */ - public XPathWrapper bindFunction(AbstractFunction func, String prefix) + public XPathWrapper bindFunction(AbstractFunction<?> func, String prefix) { _functions.addFunction(func); return bindNamespace(prefix, func.getNamespaceUri()); @@ -254,121 +252,44 @@ /** - * Applies this expression to the root of the specified document, - * converting the resulting NodeList into a java.util.List for ease - * in iteration. + * Applies this expression to the the specified node, converting the + * resulting <code>NodeList</code> into a <code>java.util.List</code>. */ - public List<Node> evaluate(Document context) + public List<Node> evaluate(Node context) { - return evaluate(context.getDocumentElement()); + return DomUtil.asList( + evaluate(context, XPathConstants.NODESET, NodeList.class), + Node.class); } /** - * Applies this expression to the specified element, converting the - * resulting NodeList into a java.util.List for ease in iteration. - */ - public List<Node> evaluate(Element context) - { - compileIfNeeded(); - try - { - NodeList result = (NodeList)_compiled.evaluate(context, XPathConstants.NODESET); - List<Node> ret = new ArrayList<Node>(result.getLength()); - for (int ii = 0 ; ii < result.getLength() ; ii++) - { - ret.add(result.item(ii)); - } - return ret; - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - - /** - * Applies this expression to the root of the specified document, - * requesting the <code>STRING</code> return type. - */ - public String evaluateAsString(Document context) - { - return evaluateAsString(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the + * Applies this expression to the specified node, requesting the * <code>STRING</code> return type. */ - public String evaluateAsString(Element context) + public String evaluateAsString(Node context) { - compileIfNeeded(); - try - { - return _compiled.evaluate(context); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } + return evaluate(context, XPathConstants.STRING, String.class); } /** - * Applies this expression to the root of the specified document, - * requesting the <code>NUMBER</code> return type. - */ - public Double evaluateAsNumber(Document context) - { - return evaluateAsNumber(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the + * Applies this expression to the specified node, requesting the * <code>NUMBER</code> return type. */ - public Double evaluateAsNumber(Element context) + public Number evaluateAsNumber(Node context) { - compileIfNeeded(); - try - { - return (Double)_compiled.evaluate(context, XPathConstants.NUMBER); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } + return evaluate(context, XPathConstants.NUMBER, Number.class); } /** - * Applies this expression to the root of the specified document, - * requesting the <code>BOOLEAN</code> return type. - */ - public Boolean evaluateAsBoolean(Document context) - { - return evaluateAsBoolean(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the + * Applies this expression to the the specified node, requesting the * <code>BOOLEAN</code> return type. */ - public Boolean evaluateAsBoolean(Element context) + public Boolean evaluateAsBoolean(Node context) { - compileIfNeeded(); - try - { - return (Boolean)_compiled.evaluate(context, XPathConstants.BOOLEAN); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } + return evaluate(context, XPathConstants.BOOLEAN, Boolean.class); } @@ -376,7 +297,6 @@ // Overrides of Object //---------------------------------------------------------------------------- - /** * Two instances are considered equal if they have the same expression, * namespace mappings, variable mappings, and function mappings. Note @@ -450,6 +370,24 @@ /** + * Compiles and executes the expression in the context of the specified + * node, returning the specified result type. + */ + private <T> T evaluate(Node context, QName returnType, Class<T> castTo) + { + compileIfNeeded(); + try + { + return castTo.cast(_compiled.evaluate(context, returnType)); + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + + /** * Resolver for variable references. */ private class MyVariableResolver Modified: trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java 2008-12-30 14:01:57 UTC (rev 69) +++ trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java 2008-12-30 14:08:10 UTC (rev 70) @@ -104,7 +104,7 @@ List<Node> result1 = xpath.evaluate(_dom); assertEquals(1, result1.size()); - assertSame(_root, result1.get(0)); + assertSame(_dom, result1.get(0)); List<Node> result2 = xpath.evaluate(_root); assertEquals(1, result2.size()); @@ -123,9 +123,7 @@ _root.setAttribute("argle", "bargle"); XPathWrapper xpath = new XPathWrapper("@foo"); - assertEquals("bar", xpath.evaluateAsString(_root)); - assertEquals("bar", xpath.evaluateAsString(_dom)); } @@ -135,9 +133,7 @@ _root.setAttribute("foo", "10"); XPathWrapper xpath = new XPathWrapper("@foo"); - - assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_root)); - assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_dom)); + assertEquals(10, xpath.evaluateAsNumber(_root).intValue()); } @@ -147,14 +143,13 @@ _root.setAttribute("foo", "10"); XPathWrapper xpath1 = new XPathWrapper("@foo=10"); - assertTrue(xpath1.evaluateAsBoolean(_root).booleanValue()); - assertTrue(xpath1.evaluateAsBoolean(_dom).booleanValue()); _root.setAttribute("foo", "20"); - assertFalse(xpath1.evaluateAsBoolean(_root).booleanValue()); - assertFalse(xpath1.evaluateAsBoolean(_dom).booleanValue()); + + XPathWrapper xpath2 = new XPathWrapper("."); + assertTrue(xpath2.evaluateAsBoolean(_root).booleanValue()); } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-30 14:02:01
|
Revision: 69 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=69&view=rev Author: kdgregory Date: 2008-12-30 14:01:57 +0000 (Tue, 30 Dec 2008) Log Message: ----------- Rename DomUtil.toList() to asList() -- consistent with Arrays.asList() Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/DomUtil.java trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java Modified: trunk/src/main/java/net/sf/practicalxml/DomUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-30 13:49:58 UTC (rev 68) +++ trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-30 14:01:57 UTC (rev 69) @@ -449,7 +449,7 @@ * @throws ClassCastException if any node in the list is not the expected * type. */ - public static <T> List<T> toList(NodeList nodelist, Class<T> ofClass) + public static <T> List<T> asList(NodeList nodelist, Class<T> ofClass) { int size = nodelist.getLength(); List<T> result = new ArrayList<T>(size); Modified: trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-30 13:49:58 UTC (rev 68) +++ trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-30 14:01:57 UTC (rev 69) @@ -318,7 +318,7 @@ DomUtil.setText(root, "blah blah blah"); Element child2 = DomUtil.appendChild(root, "foo"); - List<Element> result = DomUtil.toList(root.getElementsByTagName("*"), Element.class); + List<Element> result = DomUtil.asList(root.getElementsByTagName("*"), Element.class); assertEquals(2, result.size()); assertSame(child1, result.get(0)); assertSame(child2, result.get(1)); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-30 13:50:02
|
Revision: 68 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=68&view=rev Author: kdgregory Date: 2008-12-30 13:49:58 +0000 (Tue, 30 Dec 2008) Log Message: ----------- DomUtil.toList() Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/DomUtil.java trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java Modified: trunk/src/main/java/net/sf/practicalxml/DomUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-29 02:57:43 UTC (rev 67) +++ trunk/src/main/java/net/sf/practicalxml/DomUtil.java 2008-12-30 13:49:58 UTC (rev 68) @@ -440,6 +440,28 @@ /** + * Creates a paramaterized list from a <code>NodeList</code>, making it + * usable within the Java coding idiom. + * + * @param nodelist The list of nodes to convert. + * @param ofClass The type of the nodes being converted. + * + * @throws ClassCastException if any node in the list is not the expected + * type. + */ + public static <T> List<T> toList(NodeList nodelist, Class<T> ofClass) + { + int size = nodelist.getLength(); + List<T> result = new ArrayList<T>(size); + for (int ii = 0 ; ii < size ; ii++) + { + result.add(ofClass.cast(nodelist.item(ii))); + } + return result; + } + + + /** * Returns the path from the root of the document to the specified * element, consisting of each node's qualified name, separated by * slashes. Accepts an arbitrary number of attribute names, and @@ -697,6 +719,6 @@ if (sibling == elem) return elemPos; } - throw new IllegalArgumentException("element not amongs its siblings"); + throw new IllegalArgumentException("element not amongst its siblings"); } } Modified: trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-29 02:57:43 UTC (rev 67) +++ trunk/src/test/java/net/sf/practicalxml/TestDomUtil.java 2008-12-30 13:49:58 UTC (rev 68) @@ -309,4 +309,18 @@ // success } } + + + public void testToList() throws Exception + { + Element root = DomUtil.newDocument("foo"); + Element child1 = DomUtil.appendChild(root, "foo"); + DomUtil.setText(root, "blah blah blah"); + Element child2 = DomUtil.appendChild(root, "foo"); + + List<Element> result = DomUtil.toList(root.getElementsByTagName("*"), Element.class); + assertEquals(2, result.size()); + assertSame(child1, result.get(0)); + assertSame(child2, result.get(1)); + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-29 02:57:47
|
Revision: 67 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=67&view=rev Author: kdgregory Date: 2008-12-29 02:57:43 +0000 (Mon, 29 Dec 2008) Log Message: ----------- SchemaUtil: change access level of internal nested classes Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java Modified: trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2008-12-29 02:52:04 UTC (rev 66) +++ trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2008-12-29 02:57:43 UTC (rev 67) @@ -230,10 +230,9 @@ * 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. + * Defined as package protected so that it can be tested independently. */ - protected static class SchemaManager + static class SchemaManager { private HashMap<String,Document> _documents = new HashMap<String,Document>(); @@ -319,9 +318,9 @@ * 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. + * Defined as package-protected so that it can be tested independently. */ - protected static class SchemaComparator + static class SchemaComparator implements Comparator<Document> { public int compare(Document o1, Document o2) This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-29 02:52:08
|
Revision: 66 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=66&view=rev Author: kdgregory Date: 2008-12-29 02:52:04 +0000 (Mon, 29 Dec 2008) Log Message: ----------- XsiBoolean Added Paths: ----------- trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestXsiBoolean.java Added: trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/xpath/function/XsiBoolean.java 2008-12-29 02:52:04 UTC (rev 66) @@ -0,0 +1,69 @@ +package net.sf.practicalxml.xpath.function; + +import org.w3c.dom.Node; + +import net.sf.practicalxml.xpath.AbstractFunction; + + +/** + * Converts its argument to a boolean value, using a modification of the rules + * for Schema instances: true is represented by the literal values "true" or + * 1, ignoring case, while false is everything else. This is very different + * from the XPath function <code>boolean()</code>, in which any non-zero value + * or non-empty string/nodeset is true. + * <p> + * Note: the name of this class is <code>XsiBoolean</code>, but it's name in + * an XPath expression is "<code>boolean</code>". This is to prevent name + * collision with <code>java.lang.Boolean</code>. + */ +public class XsiBoolean +extends AbstractFunction<Boolean> +{ + public XsiBoolean() + { + super(Constants.COMMON_NS_URI, "boolean", 1); + } + + + @Override + protected Boolean processArg(int index, Node value, Boolean helper) + throws Exception + { + return (value != null) + ? processArg(index, value.getTextContent(), helper) + : Boolean.FALSE; + } + + + @Override + protected Boolean processArg(int index, String value, Boolean helper) + throws Exception + { + return "true".equals(value.toLowerCase()) + || "1".equals(value); + } + + + @Override + protected Boolean processArg(int index, Boolean value, Boolean helper) + throws Exception + { + return value; + } + + + @Override + protected Boolean processArg(int index, Number value, Boolean helper) + throws Exception + { + return Boolean.valueOf(value.intValue() == 1); + } + + + @Override + protected Boolean processNullArg(int index, Boolean helper) + throws Exception + { + return Boolean.FALSE; + } +} \ No newline at end of file Added: trunk/src/test/java/net/sf/practicalxml/xpath/function/TestXsiBoolean.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestXsiBoolean.java (rev 0) +++ trunk/src/test/java/net/sf/practicalxml/xpath/function/TestXsiBoolean.java 2008-12-29 02:52:04 UTC (rev 66) @@ -0,0 +1,183 @@ +package net.sf.practicalxml.xpath.function; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import javax.xml.xpath.XPathFunctionException; +import org.w3c.dom.Element; +import org.w3c.dom.Node; + +import net.sf.practicalxml.AbstractTestCase; +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.xpath.XPathWrapper; + + +public class TestXsiBoolean +extends AbstractTestCase +{ + public TestXsiBoolean(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Setup +//---------------------------------------------------------------------------- + + private Element _root; + private Element _child1; + private Element _child2; + private Element _child3; + private Element _child4; + private Element _child5; + + @Override + protected void setUp() throws Exception + { + _root = DomUtil.newDocument("foo"); + _child1 = DomUtil.appendChild(_root, "bar"); + _child2 = DomUtil.appendChild(_root, "baz"); + _child3 = DomUtil.appendChild(_root, "false"); + _child4 = DomUtil.appendChild(_root, "true"); + _child5 = DomUtil.appendChild(_root, "x"); + + DomUtil.setText(_child1, "true"); + DomUtil.setText(_child2, "false"); + DomUtil.setText(_child3, "1"); + DomUtil.setText(_child4, "0"); + + _child1.setAttribute("attr", "False"); + _child2.setAttribute("attr", "True"); + _child3.setAttribute("attr", "0"); + _child4.setAttribute("attr", "1"); + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testConstruction() throws Exception + { + XsiBoolean fn = new XsiBoolean(); + assertEquals(Constants.COMMON_NS_URI, fn.getNamespaceUri()); + assertEquals("boolean", fn.getName()); + assertEquals(1, fn.getMinArgCount()); + assertEquals(1, fn.getMaxArgCount()); + } + + + public void testLiteralString() throws Exception + { + assertEquals( + Boolean.TRUE, + new XsiBoolean().evaluate(Arrays.asList("true"))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList("false"))); + assertEquals( + Boolean.TRUE, + new XsiBoolean().evaluate(Arrays.asList("TrUe"))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList("FaLsE"))); + assertEquals( + Boolean.TRUE, + new XsiBoolean().evaluate(Arrays.asList("1"))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList("0"))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList("zippy"))); + } + + + public void testLiteralNumber() throws Exception + { + assertEquals( + Boolean.TRUE, + new XsiBoolean().evaluate(Arrays.asList(Double.valueOf(1)))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList(Double.valueOf(0)))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList(Double.valueOf(10)))); + } + + + public void testNodeList() throws Exception + { + assertEquals( + Boolean.TRUE, + new XsiBoolean().evaluate(Arrays.asList(_root.getChildNodes()))); + } + + + public void testNode() throws Exception + { + assertEquals( + Boolean.TRUE, + new XsiBoolean().evaluate(Arrays.asList(_child1))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList(_child2))); + assertEquals( + Boolean.TRUE, + new XsiBoolean().evaluate(Arrays.asList(_child3))); + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList(_child4))); + } + + + public void testEmptyNodeList() throws Exception + { + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList(_child5.getChildNodes()))); + } + + + public void testNull() throws Exception + { + assertEquals( + Boolean.FALSE, + new XsiBoolean().evaluate(Arrays.asList((String)null))); + } + + + public void testEmptyArglist() throws Exception + { + try + { + new XsiBoolean().evaluate(Collections.<String>emptyList()); + fail("didn't throw on empty list"); + } + catch (XPathFunctionException e) + { + // success + } + } + + + public void testInSitu() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("//*[ns:boolean(text())]") + .bindFunction(new XsiBoolean(), "ns"); + List<Node> result1 = xpath1.evaluate(_root); + assertEquals(2, result1.size()); + assertSame(_child1, result1.get(0)); + assertSame(_child3, result1.get(1)); + + XPathWrapper xpath2 = new XPathWrapper("//*[ns:boolean(@attr)]") + .bindFunction(new XsiBoolean(), "ns"); + List<Node> result2 = xpath2.evaluate(_root); + assertEquals(2, result2.size()); + assertSame(_child2, result2.get(0)); + assertSame(_child4, result2.get(1)); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-29 01:56:42
|
Revision: 65 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=65&view=rev Author: kdgregory Date: 2008-12-29 01:31:37 +0000 (Mon, 29 Dec 2008) Log Message: ----------- AbstractFunction: wasn't handling Number arguments Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java Modified: trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java 2008-12-29 00:42:26 UTC (rev 64) +++ trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java 2008-12-29 01:31:37 UTC (rev 65) @@ -280,6 +280,8 @@ { if (arg instanceof String) helper = processArg(idx, (String)arg, helper); + else if (arg instanceof Number) + helper = processArg(idx, (Number)arg, helper); else if (arg instanceof NodeList) helper = processArg(idx, (NodeList)arg, helper); else if (arg instanceof Node) Modified: trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java 2008-12-29 00:42:26 UTC (rev 64) +++ trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java 2008-12-29 01:31:37 UTC (rev 65) @@ -228,6 +228,22 @@ } + public void testEvaluateNumber() throws Exception + { + List args = Arrays.asList(new Object[] {Integer.valueOf(10)}); + MyMockFunction fn = new MyMockFunction("zippy"); + + assertEquals("zippy", fn.evaluate(args)); + assertEquals(1, fn._initCalls); + assertEquals(0, fn._processStringCalls); + assertEquals(1, fn._processNumberCalls); + assertEquals(0, fn._processBooleanCalls); + assertEquals(0, fn._procesNodeCalls); + assertEquals(0, fn._procesNodeListCalls); + assertEquals(1, fn._getResultCalls); + } + + public void testEvaluateNode() throws Exception { // since any JDK Node implementation is also a NodeList, we have to This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-29 00:42:29
|
Revision: 64 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=64&view=rev Author: kdgregory Date: 2008-12-29 00:42:26 +0000 (Mon, 29 Dec 2008) Log Message: ----------- XPathWrapper: move to xpath package Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java Added Paths: ----------- trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java Removed Paths: ------------- trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java Deleted: trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -1,465 +0,0 @@ -package net.sf.practicalxml; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.xml.namespace.QName; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathExpression; -import javax.xml.xpath.XPathExpressionException; -import javax.xml.xpath.XPathFactory; -import javax.xml.xpath.XPathFunction; -import javax.xml.xpath.XPathVariableResolver; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import net.sf.practicalxml.xpath.AbstractFunction; -import net.sf.practicalxml.xpath.FunctionResolver; -import net.sf.practicalxml.xpath.NamespaceResolver; - - - -/** - * This class simplifies the use of XPath expressions, hiding the factory and - * return types, and providing a simple builder-style interface for adding - * resolvers. It also maintains the expression in a compiled form, improving - * reuse performance. - */ -public class XPathWrapper -{ - private final String _expr; - private final NamespaceResolver _nsResolver = new NamespaceResolver(); - private Map<QName,Object> _variables = new HashMap<QName,Object>(); - private FunctionResolver _functions = new FunctionResolver(); - - private XPathExpression _compiled; - - - /** - * Creates a new instance, which may then be customized with various - * resolvers, and used to evaluate expressions. - */ - public XPathWrapper(String expr) - { - _expr = expr; - } - - -//---------------------------------------------------------------------------- -// Public methods -//---------------------------------------------------------------------------- - - /** - * Adds a namespace binding to this expression. All bindings must be - * added prior to the first call to <code>evaluate()</code>. - * - * @param prefix The prefix used to reference this namespace in the - * XPath expression. Note that this does <em>not</em> - * need to be the same prefix used by the document. - * @param nsURI The namespace URI to associate with this prefix. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindNamespace(String prefix, String nsURI) - { - _nsResolver.addNamespace(prefix, nsURI); - return this; - } - - - /** - * Sets the default namespace binding: this will be applied to all - * expressions that do not explicitly specify a prefix (although - * they must use the colon separating the non-existent prefix from - * the element name). - * - * @param nsURI The default namespace for this document. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindDefaultNamespace(String nsURI) - { - _nsResolver.setDefaultNamespace(nsURI); - return this; - } - - - /** - * Binds a value to a variable, replacing any previous value for that - * variable. Unlike other configuration methods, this may be called - * after calling <code>evaluate()</code>; the new values will be used - * for subsequent evaluations. - * - * @param name The name of the variable; this is turned into a - * <code>QName</code> without namespace. - * @param value The value of the variable; the XPath evaluator must - * be able to convert this value into a type usable in - * an XPath expression. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindVariable(String name, Object value) - { - return bindVariable(new QName(name), value); - } - - - /** - * Binds a value to a variable, replacing any previous value for that - * variable. Unlike other configuration methods, this may be called - * after calling <code>evaluate()</code>; the new values will be used - * for subsequent evaluations. - * - * @param name The fully-qualified name of the variable. - * @param value The value of the variable; the XPath evaluator must - * be able to convert this value into a type usable in - * an XPath expression. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindVariable(QName name, Object value) - { - _variables.put(name, value); - return this; - } - - - /** - * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this - * expression. Subsequent calls to this method with the same name will - * silently replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. You must bind a namespace for this function, and use - * the bound prefix to reference it in your expression. Alternatively, - * you can call the variant of <code>bindFunction()</code> that binds - * a prefix to the function's namespace. - * - * @param func The function. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(AbstractFunction func) - { - _functions.addFunction(func); - return this; - } - - - /** - * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this - * expression, along with the prefix used to access that function. - * Subsequent calls to this method with the same name will silently - * replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. This method will retrieve the namespace from the - * function, and bind it to the passed prefix. - * - * @param func The function. - * @param prefix The prefix to bind to this function's namespace. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(AbstractFunction func, String prefix) - { - _functions.addFunction(func); - return bindNamespace(prefix, func.getNamespaceUri()); - } - - - /** - * Binds a standard <code>XPathFunction</code> to this expression, - * handling any number of arguments. Subsequent calls to this method - * with the same name will silently replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. If the qualified name that you pass to this method - * includes a prefix, the associated namespace will be bound to that - * prefix. If not, you must bind the namespace explicitly. In either - * case, you must refer to the function in your expression using a - * bound prefix. - * - * @param name The qualified name for this function. Must contain - * a name and namespace, may contain a prefix. - * @param func The function to bind to this name. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(QName name, XPathFunction func) - { - return bindFunction(name, func, 0, Integer.MAX_VALUE); - } - - - /** - * Binds a standard <code>XPathFunction</code> to this expression, - * handling a specific number of arguments. Subsequent calls to this - * method with the same name and arity will silently replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. If the qualified name that you pass to this method - * includes a prefix, the associated namespace will be bound to that - * prefix. If not, you must bind the namespace explicitly. In either - * case, you must refer to the function in your expression using a - * bound prefix. - * - * @param name The qualified name for this function. Must contain - * a name and namespace, may contain a prefix. - * @param func The function to bind to this name. - * @param arity The number of arguments accepted by this function. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(QName name, XPathFunction func, int arity) - { - return bindFunction(name, func, arity, arity); - } - - - /** - * Binds a standard <code>XPathFunction</code> to this expression, - * handling a specific range of arguments. Subsequent calls to this - * method with the same name and range of arguments will silently - * replace the binding. - * <p> - * Per the JDK documentation, user-defined functions must occupy their - * own namespace. If the qualified name that you pass to this method - * includes a prefix, the associated namespace will be bound to that - * prefix. If not, you must bind the namespace explicitly. In either - * case, you must refer to the function in your expression using a - * bound prefix. - * - * @param name The qualified name for this function. Must contain - * a name and namespace, may contain a prefix. - * @param func The function to bind to this name. - * @param minArity The minimum number of arguments accepted by this - * function. - * @param maxArity The maximum number of arguments accepted by this - * function. - * - * @return The wrapper, so that calls may be chained. - */ - public XPathWrapper bindFunction(QName name, XPathFunction func, - int minArity, int maxArity) - { - _functions.addFunction(func, name, minArity, maxArity); - if (!"".equals(name.getPrefix())) - bindNamespace(name.getPrefix(), name.getNamespaceURI()); - return this; - } - - - /** - * Applies this expression to the root of the specified document, - * converting the resulting NodeList into a java.util.List for ease - * in iteration. - */ - public List<Node> evaluate(Document context) - { - return evaluate(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, converting the - * resulting NodeList into a java.util.List for ease in iteration. - */ - public List<Node> evaluate(Element context) - { - compileIfNeeded(); - try - { - NodeList result = (NodeList)_compiled.evaluate(context, XPathConstants.NODESET); - List<Node> ret = new ArrayList<Node>(result.getLength()); - for (int ii = 0 ; ii < result.getLength() ; ii++) - { - ret.add(result.item(ii)); - } - return ret; - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - - /** - * Applies this expression to the root of the specified document, - * requesting the <code>STRING</code> return type. - */ - public String evaluateAsString(Document context) - { - return evaluateAsString(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the - * <code>STRING</code> return type. - */ - public String evaluateAsString(Element context) - { - compileIfNeeded(); - try - { - return _compiled.evaluate(context); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - - /** - * Applies this expression to the root of the specified document, - * requesting the <code>NUMBER</code> return type. - */ - public Double evaluateAsNumber(Document context) - { - return evaluateAsNumber(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the - * <code>NUMBER</code> return type. - */ - public Double evaluateAsNumber(Element context) - { - compileIfNeeded(); - try - { - return (Double)_compiled.evaluate(context, XPathConstants.NUMBER); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - - /** - * Applies this expression to the root of the specified document, - * requesting the <code>BOOLEAN</code> return type. - */ - public Boolean evaluateAsBoolean(Document context) - { - return evaluateAsBoolean(context.getDocumentElement()); - } - - - /** - * Applies this expression to the specified element, requesting the - * <code>BOOLEAN</code> return type. - */ - public Boolean evaluateAsBoolean(Element context) - { - compileIfNeeded(); - try - { - return (Boolean)_compiled.evaluate(context, XPathConstants.BOOLEAN); - } - catch (Exception ee) - { - throw new XmlException("unable to evaluate: " + _expr, ee); - } - } - - -//---------------------------------------------------------------------------- -// Overrides of Object -//---------------------------------------------------------------------------- - - - /** - * Two instances are considered equal if they have the same expression, - * namespace mappings, variable mappings, and function mappings. Note - * that instances with function mappings are all but guaranteed to be - * not-equal, unless you override the <code>equals()</code> method on - * the function implementation class. - */ - @Override - public final boolean equals(Object obj) - { - if (obj instanceof XPathWrapper) - { - XPathWrapper that = (XPathWrapper)obj; - return this._expr.equals(that._expr) - && this._nsResolver.equals(that._nsResolver) - && this._variables.equals(that._variables) - && this._functions.equals(that._functions); - } - return false; - } - - - /** - * Hash code is driven by the expression, ignoring differences between - * namespaces, variables, and functions. - */ - @Override - public int hashCode() - { - return _expr.hashCode(); - } - - - /** - * The string value is the expression. - */ - @Override - public String toString() - { - return _expr; - } - - -//---------------------------------------------------------------------------- -// Internals -//---------------------------------------------------------------------------- - - /** - * Compiles the expression, if it has not already been compiled. This is - * called from the various <code>evaluate</code> methods, and ensures - * that the caller has completely configured the wrapper prior to use. - */ - private void compileIfNeeded() - { - if (_compiled != null) - return; - - try - { - XPath xpath = XPathFactory.newInstance().newXPath(); - xpath.setNamespaceContext(_nsResolver); - xpath.setXPathVariableResolver(new MyVariableResolver()); - xpath.setXPathFunctionResolver(_functions); - _compiled = xpath.compile(_expr); - } - catch (XPathExpressionException ee) - { - throw new XmlException("unable to compile: " + _expr, ee); - } - } - - - /** - * Resolver for variable references. - */ - private class MyVariableResolver - implements XPathVariableResolver - { - public Object resolveVariable(QName name) - { - return _variables.get(name); - } - } -} Modified: trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/main/java/net/sf/practicalxml/junit/DomAsserts.java 2008-12-29 00:42:26 UTC (rev 64) @@ -7,7 +7,7 @@ import junit.framework.Assert; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; import org.w3c.dom.Element; import org.w3c.dom.Node; Copied: trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java (from rev 63, trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java) =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/xpath/XPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -0,0 +1,463 @@ +package net.sf.practicalxml.xpath; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import javax.xml.namespace.QName; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; +import javax.xml.xpath.XPathFunction; +import javax.xml.xpath.XPathVariableResolver; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import net.sf.practicalxml.XmlException; + + + +/** + * This class simplifies the use of XPath expressions, hiding the factory and + * return types, and providing a simple builder-style interface for adding + * resolvers. It also maintains the expression in a compiled form, improving + * reuse performance. + */ +public class XPathWrapper +{ + private final String _expr; + private final NamespaceResolver _nsResolver = new NamespaceResolver(); + private Map<QName,Object> _variables = new HashMap<QName,Object>(); + private FunctionResolver _functions = new FunctionResolver(); + + private XPathExpression _compiled; + + + /** + * Creates a new instance, which may then be customized with various + * resolvers, and used to evaluate expressions. + */ + public XPathWrapper(String expr) + { + _expr = expr; + } + + +//---------------------------------------------------------------------------- +// Public methods +//---------------------------------------------------------------------------- + + /** + * Adds a namespace binding to this expression. All bindings must be + * added prior to the first call to <code>evaluate()</code>. + * + * @param prefix The prefix used to reference this namespace in the + * XPath expression. Note that this does <em>not</em> + * need to be the same prefix used by the document. + * @param nsURI The namespace URI to associate with this prefix. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindNamespace(String prefix, String nsURI) + { + _nsResolver.addNamespace(prefix, nsURI); + return this; + } + + + /** + * Sets the default namespace binding: this will be applied to all + * expressions that do not explicitly specify a prefix (although + * they must use the colon separating the non-existent prefix from + * the element name). + * + * @param nsURI The default namespace for this document. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindDefaultNamespace(String nsURI) + { + _nsResolver.setDefaultNamespace(nsURI); + return this; + } + + + /** + * Binds a value to a variable, replacing any previous value for that + * variable. Unlike other configuration methods, this may be called + * after calling <code>evaluate()</code>; the new values will be used + * for subsequent evaluations. + * + * @param name The name of the variable; this is turned into a + * <code>QName</code> without namespace. + * @param value The value of the variable; the XPath evaluator must + * be able to convert this value into a type usable in + * an XPath expression. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindVariable(String name, Object value) + { + return bindVariable(new QName(name), value); + } + + + /** + * Binds a value to a variable, replacing any previous value for that + * variable. Unlike other configuration methods, this may be called + * after calling <code>evaluate()</code>; the new values will be used + * for subsequent evaluations. + * + * @param name The fully-qualified name of the variable. + * @param value The value of the variable; the XPath evaluator must + * be able to convert this value into a type usable in + * an XPath expression. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindVariable(QName name, Object value) + { + _variables.put(name, value); + return this; + } + + + /** + * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this + * expression. Subsequent calls to this method with the same name will + * silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. You must bind a namespace for this function, and use + * the bound prefix to reference it in your expression. Alternatively, + * you can call the variant of <code>bindFunction()</code> that binds + * a prefix to the function's namespace. + * + * @param func The function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(AbstractFunction func) + { + _functions.addFunction(func); + return this; + } + + + /** + * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this + * expression, along with the prefix used to access that function. + * Subsequent calls to this method with the same name will silently + * replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. This method will retrieve the namespace from the + * function, and bind it to the passed prefix. + * + * @param func The function. + * @param prefix The prefix to bind to this function's namespace. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(AbstractFunction func, String prefix) + { + _functions.addFunction(func); + return bindNamespace(prefix, func.getNamespaceUri()); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling any number of arguments. Subsequent calls to this method + * with the same name will silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func) + { + return bindFunction(name, func, 0, Integer.MAX_VALUE); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling a specific number of arguments. Subsequent calls to this + * method with the same name and arity will silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * @param arity The number of arguments accepted by this function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func, int arity) + { + return bindFunction(name, func, arity, arity); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling a specific range of arguments. Subsequent calls to this + * method with the same name and range of arguments will silently + * replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * @param minArity The minimum number of arguments accepted by this + * function. + * @param maxArity The maximum number of arguments accepted by this + * function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func, + int minArity, int maxArity) + { + _functions.addFunction(func, name, minArity, maxArity); + if (!"".equals(name.getPrefix())) + bindNamespace(name.getPrefix(), name.getNamespaceURI()); + return this; + } + + + /** + * Applies this expression to the root of the specified document, + * converting the resulting NodeList into a java.util.List for ease + * in iteration. + */ + public List<Node> evaluate(Document context) + { + return evaluate(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, converting the + * resulting NodeList into a java.util.List for ease in iteration. + */ + public List<Node> evaluate(Element context) + { + compileIfNeeded(); + try + { + NodeList result = (NodeList)_compiled.evaluate(context, XPathConstants.NODESET); + List<Node> ret = new ArrayList<Node>(result.getLength()); + for (int ii = 0 ; ii < result.getLength() ; ii++) + { + ret.add(result.item(ii)); + } + return ret; + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + + /** + * Applies this expression to the root of the specified document, + * requesting the <code>STRING</code> return type. + */ + public String evaluateAsString(Document context) + { + return evaluateAsString(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, requesting the + * <code>STRING</code> return type. + */ + public String evaluateAsString(Element context) + { + compileIfNeeded(); + try + { + return _compiled.evaluate(context); + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + + /** + * Applies this expression to the root of the specified document, + * requesting the <code>NUMBER</code> return type. + */ + public Double evaluateAsNumber(Document context) + { + return evaluateAsNumber(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, requesting the + * <code>NUMBER</code> return type. + */ + public Double evaluateAsNumber(Element context) + { + compileIfNeeded(); + try + { + return (Double)_compiled.evaluate(context, XPathConstants.NUMBER); + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + + /** + * Applies this expression to the root of the specified document, + * requesting the <code>BOOLEAN</code> return type. + */ + public Boolean evaluateAsBoolean(Document context) + { + return evaluateAsBoolean(context.getDocumentElement()); + } + + + /** + * Applies this expression to the specified element, requesting the + * <code>BOOLEAN</code> return type. + */ + public Boolean evaluateAsBoolean(Element context) + { + compileIfNeeded(); + try + { + return (Boolean)_compiled.evaluate(context, XPathConstants.BOOLEAN); + } + catch (Exception ee) + { + throw new XmlException("unable to evaluate: " + _expr, ee); + } + } + + +//---------------------------------------------------------------------------- +// Overrides of Object +//---------------------------------------------------------------------------- + + + /** + * Two instances are considered equal if they have the same expression, + * namespace mappings, variable mappings, and function mappings. Note + * that instances with function mappings are all but guaranteed to be + * not-equal, unless you override the <code>equals()</code> method on + * the function implementation class. + */ + @Override + public final boolean equals(Object obj) + { + if (obj instanceof XPathWrapper) + { + XPathWrapper that = (XPathWrapper)obj; + return this._expr.equals(that._expr) + && this._nsResolver.equals(that._nsResolver) + && this._variables.equals(that._variables) + && this._functions.equals(that._functions); + } + return false; + } + + + /** + * Hash code is driven by the expression, ignoring differences between + * namespaces, variables, and functions. + */ + @Override + public int hashCode() + { + return _expr.hashCode(); + } + + + /** + * The string value is the expression. + */ + @Override + public String toString() + { + return _expr; + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Compiles the expression, if it has not already been compiled. This is + * called from the various <code>evaluate</code> methods, and ensures + * that the caller has completely configured the wrapper prior to use. + */ + private void compileIfNeeded() + { + if (_compiled != null) + return; + + try + { + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(_nsResolver); + xpath.setXPathVariableResolver(new MyVariableResolver()); + xpath.setXPathFunctionResolver(_functions); + _compiled = xpath.compile(_expr); + } + catch (XPathExpressionException ee) + { + throw new XmlException("unable to compile: " + _expr, ee); + } + } + + + /** + * Resolver for variable references. + */ + private class MyVariableResolver + implements XPathVariableResolver + { + public Object resolveVariable(QName name) + { + return _variables.get(name); + } + } +} Deleted: trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -1,339 +0,0 @@ -package net.sf.practicalxml; - -import java.util.List; -import javax.xml.namespace.QName; -import javax.xml.xpath.XPathFunction; -import javax.xml.xpath.XPathFunctionException; - -import net.sf.practicalxml.xpath.AbstractFunction; - -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - - -public class TestXPathWrapper -extends AbstractTestCase -{ - public TestXPathWrapper(String name) - { - super(name); - } - - -//---------------------------------------------------------------------------- -// Test data -//---------------------------------------------------------------------------- - - public final static String EL_ROOT = "root"; - public final static String EL_CHILD = "child"; - public final static String NS1 = "ns1"; - public final static String NS2 = "ns2"; - - Document _dom; - Element _root; - Element _child1; - Element _child2; - Element _child3; - - @Override - protected void setUp() - { - _root = DomUtil.newDocument(EL_ROOT); - _child1 = DomUtil.appendChild(_root, EL_CHILD); - _child2 = DomUtil.appendChild(_root, NS1, EL_CHILD); - _child3 = DomUtil.appendChild(_root, NS2, EL_CHILD); - _dom = _root.getOwnerDocument(); - } - - -//---------------------------------------------------------------------------- -// Support Code -//---------------------------------------------------------------------------- - - /** - * A standard XPath function implementation that returns the namespace - * of the first selected node. - */ - private static class MyStandardFunction - implements XPathFunction - { - public Object evaluate(List args) - throws XPathFunctionException - { - NodeList arg = (NodeList)args.get(0); - return arg.item(0).getNamespaceURI(); - } - } - - - /** - * An <code>AbstractFunction</code> implementation that returns the - * namespace of the first selected node. - */ - private static class MyAbstractFunction - extends AbstractFunction<String> - { - public MyAbstractFunction(String nsUri, String name) - { - super(nsUri, name); - } - - @Override - protected String processArg(int index, Node value, String helper) - throws Exception - { - return value.getNamespaceURI(); - } - } - -//---------------------------------------------------------------------------- -// Test Cases -//---------------------------------------------------------------------------- - - // the basic test to verify we can compile and execute - public void testCurrentElement() - throws Exception - { - XPathWrapper xpath = new XPathWrapper("."); - - List<Node> result1 = xpath.evaluate(_dom); - assertEquals(1, result1.size()); - assertSame(_root, result1.get(0)); - - List<Node> result2 = xpath.evaluate(_root); - assertEquals(1, result2.size()); - assertSame(_root, result2.get(0)); - - List<Node> result3 = xpath.evaluate(_child1); - assertEquals(1, result3.size()); - assertSame(_child1, result3.get(0)); - } - - - public void testEvalAsString() - throws Exception - { - _root.setAttribute("foo", "bar"); - _root.setAttribute("argle", "bargle"); - - XPathWrapper xpath = new XPathWrapper("@foo"); - - assertEquals("bar", xpath.evaluateAsString(_root)); - assertEquals("bar", xpath.evaluateAsString(_dom)); - } - - - public void testEvalAsNumber() - throws Exception - { - _root.setAttribute("foo", "10"); - - XPathWrapper xpath = new XPathWrapper("@foo"); - - assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_root)); - assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_dom)); - } - - - public void testEvalAsBoolean() - throws Exception - { - _root.setAttribute("foo", "10"); - - XPathWrapper xpath1 = new XPathWrapper("@foo=10"); - - assertTrue(xpath1.evaluateAsBoolean(_root).booleanValue()); - assertTrue(xpath1.evaluateAsBoolean(_dom).booleanValue()); - - _root.setAttribute("foo", "20"); - - assertFalse(xpath1.evaluateAsBoolean(_root).booleanValue()); - assertFalse(xpath1.evaluateAsBoolean(_dom).booleanValue()); - } - - - public void testNamespaces() throws Exception - { - XPathWrapper xpath1 = new XPathWrapper("//child"); - List<Node> result1 = xpath1.evaluate(_dom); - assertEquals(1, result1.size()); - assertSame(_child1, result1.get(0)); - - XPathWrapper xpath2 = new XPathWrapper("//ns:child") - .bindNamespace("ns", NS1); - List<Node> result2 = xpath2.evaluate(_dom); - assertEquals(1, result2.size()); - assertSame(_child2, result2.get(0)); - - XPathWrapper xpath3 = new XPathWrapper("//:child") - .bindDefaultNamespace(NS2); - List<Node> result3 = xpath3.evaluate(_dom); - assertEquals(1, result3.size()); - assertSame(_child3, result3.get(0)); - } - - - public void testVariables() throws Exception - { - _child1.setAttribute("bar", "baz"); - _child2.setAttribute("bar", "bargle"); - - XPathWrapper xpath = new XPathWrapper("//*[@bar=$test]") - .bindVariable(new QName("test"), "baz"); - - List<Node> result1 = xpath.evaluate(_dom); - assertEquals(1, result1.size()); - assertSame(_child1, result1.get(0)); - - xpath.bindVariable("test", "bargle"); - - List<Node> result2 = xpath.evaluate(_dom); - assertEquals(1, result2.size()); - assertSame(_child2, result2.get(0)); - } - - - public void testAbstractFunctions() throws Exception - { - XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") - .bindNamespace("ns", NS1) - .bindFunction(new MyAbstractFunction(NS1, "myfunc")); - - assertEquals("", xpath1.evaluateAsString(_child1)); - assertEquals(NS1, xpath1.evaluateAsString(_child2)); - - XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.)") - .bindFunction(new MyAbstractFunction(NS1, "myfunc"), "ns"); - - assertEquals("", xpath2.evaluateAsString(_child1)); - assertEquals(NS1, xpath2.evaluateAsString(_child2)); - } - - - public void testStandardFunctions() throws Exception - { - XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") - .bindFunction(new QName(NS1, "myfunc", "ns"), - new MyStandardFunction()); - - assertEquals("", xpath1.evaluateAsString(_child1)); - assertEquals(NS1, xpath1.evaluateAsString(_child2)); - - XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.,.)") - .bindFunction(new QName(NS2, "myfunc", "ns"), - new MyStandardFunction(), - 2); - - assertEquals("", xpath2.evaluateAsString(_child1)); - assertEquals(NS1, xpath2.evaluateAsString(_child2)); - } - - - public void testUnresolvableFunction() throws Exception - { - // we call with two arguments, it only gets resolved for one - XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.,.)") - .bindFunction(new QName(NS2, "myfunc", "ns"), - new MyStandardFunction(), - 1); - try - { - xpath1.evaluateAsString(_child1); - fail("didn't throw even though arity was wrong"); - } - catch (XmlException ee) - { - // success - } - } - - - - public void testEqualsAndHashCode() throws Exception - { - Object obj1a = new XPathWrapper("//foo"); - Object obj1b = new XPathWrapper("//foo"); - Object obj2a = new XPathWrapper("//foo") - .bindDefaultNamespace("zippy"); - Object obj2b = new XPathWrapper("//foo") - .bindDefaultNamespace("zippy"); - Object obj3a = new XPathWrapper("//foo") - .bindNamespace("argle", "bargle"); - Object obj3b = new XPathWrapper("//foo") - .bindNamespace("argle", "bargle"); - Object obj4a = new XPathWrapper("//foo") - .bindVariable("argle", "bargle"); - Object obj4b = new XPathWrapper("//foo") - .bindVariable("argle", "bargle"); - Object obj5a = new XPathWrapper("//foo") - .bindFunction(new QName("foo"), null); - Object obj5b = new XPathWrapper("//foo") - .bindFunction(new QName("foo"), null); - - assertFalse(obj1a.equals(null)); - assertFalse(obj1a.equals(new Object())); - - assertTrue(obj1a.equals(obj1b)); - assertTrue(obj1b.equals(obj1a)); - assertEquals(obj1a.hashCode(), obj1b.hashCode()); - - assertFalse(obj1a.equals(obj2a)); - assertTrue(obj2a.equals(obj2b)); - assertTrue(obj2b.equals(obj2a)); - assertEquals(obj1a.hashCode(), obj2a.hashCode()); - assertEquals(obj2a.hashCode(), obj2b.hashCode()); - - assertFalse(obj1a.equals(obj3a)); - assertTrue(obj3a.equals(obj3b)); - assertTrue(obj3b.equals(obj3a)); - assertEquals(obj1a.hashCode(), obj3a.hashCode()); - assertEquals(obj3a.hashCode(), obj3b.hashCode()); - - assertFalse(obj1a.equals(obj4a)); - assertTrue(obj4a.equals(obj4b)); - assertTrue(obj4b.equals(obj4a)); - assertEquals(obj1a.hashCode(), obj4a.hashCode()); - assertEquals(obj4a.hashCode(), obj4b.hashCode()); - - assertFalse(obj1a.equals(obj5a)); - assertTrue(obj5a.equals(obj5b)); - assertTrue(obj5b.equals(obj5a)); - assertEquals(obj1a.hashCode(), obj5a.hashCode()); - assertEquals(obj5a.hashCode(), obj5b.hashCode()); - } - - - public void testToString() throws Exception - { - final String expr = "//foo"; - assertEquals(expr, new XPathWrapper(expr).toString()); - assertEquals(expr, new XPathWrapper(expr).bindNamespace("foo", "bar").toString()); - } - - - public void testFailures() throws Exception - { - try - { - new XPathWrapper(".foo.").evaluate(_dom); - fail("compiled invalid expression"); - } - catch (XmlException ee) - { - // success - } - - try - { - new XPathWrapper("@foo=$bar").evaluate(_dom); - fail("evaluated expression with unbound variable"); - } - catch (XmlException ee) - { - // success - } - } - -} Modified: trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/junit/TestDomAsserts.java 2008-12-29 00:42:26 UTC (rev 64) @@ -7,7 +7,7 @@ import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; public class TestDomAsserts Copied: trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java (from rev 63, trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java) =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java (rev 0) +++ trunk/src/test/java/net/sf/practicalxml/xpath/TestXPathWrapper.java 2008-12-29 00:42:26 UTC (rev 64) @@ -0,0 +1,343 @@ +package net.sf.practicalxml.xpath; + +import java.util.List; +import javax.xml.namespace.QName; +import javax.xml.xpath.XPathFunction; +import javax.xml.xpath.XPathFunctionException; + +import net.sf.practicalxml.AbstractTestCase; +import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XmlException; +import net.sf.practicalxml.xpath.AbstractFunction; +import net.sf.practicalxml.xpath.XPathWrapper; + +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + + +public class TestXPathWrapper +extends AbstractTestCase +{ + public TestXPathWrapper(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Test data +//---------------------------------------------------------------------------- + + public final static String EL_ROOT = "root"; + public final static String EL_CHILD = "child"; + public final static String NS1 = "ns1"; + public final static String NS2 = "ns2"; + + Document _dom; + Element _root; + Element _child1; + Element _child2; + Element _child3; + + @Override + protected void setUp() + { + _root = DomUtil.newDocument(EL_ROOT); + _child1 = DomUtil.appendChild(_root, EL_CHILD); + _child2 = DomUtil.appendChild(_root, NS1, EL_CHILD); + _child3 = DomUtil.appendChild(_root, NS2, EL_CHILD); + _dom = _root.getOwnerDocument(); + } + + +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + /** + * A standard XPath function implementation that returns the namespace + * of the first selected node. + */ + private static class MyStandardFunction + implements XPathFunction + { + public Object evaluate(List args) + throws XPathFunctionException + { + NodeList arg = (NodeList)args.get(0); + return arg.item(0).getNamespaceURI(); + } + } + + + /** + * An <code>AbstractFunction</code> implementation that returns the + * namespace of the first selected node. + */ + private static class MyAbstractFunction + extends AbstractFunction<String> + { + public MyAbstractFunction(String nsUri, String name) + { + super(nsUri, name); + } + + @Override + protected String processArg(int index, Node value, String helper) + throws Exception + { + return value.getNamespaceURI(); + } + } + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + // the basic test to verify we can compile and execute + public void testCurrentElement() + throws Exception + { + XPathWrapper xpath = new XPathWrapper("."); + + List<Node> result1 = xpath.evaluate(_dom); + assertEquals(1, result1.size()); + assertSame(_root, result1.get(0)); + + List<Node> result2 = xpath.evaluate(_root); + assertEquals(1, result2.size()); + assertSame(_root, result2.get(0)); + + List<Node> result3 = xpath.evaluate(_child1); + assertEquals(1, result3.size()); + assertSame(_child1, result3.get(0)); + } + + + public void testEvalAsString() + throws Exception + { + _root.setAttribute("foo", "bar"); + _root.setAttribute("argle", "bargle"); + + XPathWrapper xpath = new XPathWrapper("@foo"); + + assertEquals("bar", xpath.evaluateAsString(_root)); + assertEquals("bar", xpath.evaluateAsString(_dom)); + } + + + public void testEvalAsNumber() + throws Exception + { + _root.setAttribute("foo", "10"); + + XPathWrapper xpath = new XPathWrapper("@foo"); + + assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_root)); + assertEquals(Double.valueOf(10.0), xpath.evaluateAsNumber(_dom)); + } + + + public void testEvalAsBoolean() + throws Exception + { + _root.setAttribute("foo", "10"); + + XPathWrapper xpath1 = new XPathWrapper("@foo=10"); + + assertTrue(xpath1.evaluateAsBoolean(_root).booleanValue()); + assertTrue(xpath1.evaluateAsBoolean(_dom).booleanValue()); + + _root.setAttribute("foo", "20"); + + assertFalse(xpath1.evaluateAsBoolean(_root).booleanValue()); + assertFalse(xpath1.evaluateAsBoolean(_dom).booleanValue()); + } + + + public void testNamespaces() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("//child"); + List<Node> result1 = xpath1.evaluate(_dom); + assertEquals(1, result1.size()); + assertSame(_child1, result1.get(0)); + + XPathWrapper xpath2 = new XPathWrapper("//ns:child") + .bindNamespace("ns", NS1); + List<Node> result2 = xpath2.evaluate(_dom); + assertEquals(1, result2.size()); + assertSame(_child2, result2.get(0)); + + XPathWrapper xpath3 = new XPathWrapper("//:child") + .bindDefaultNamespace(NS2); + List<Node> result3 = xpath3.evaluate(_dom); + assertEquals(1, result3.size()); + assertSame(_child3, result3.get(0)); + } + + + public void testVariables() throws Exception + { + _child1.setAttribute("bar", "baz"); + _child2.setAttribute("bar", "bargle"); + + XPathWrapper xpath = new XPathWrapper("//*[@bar=$test]") + .bindVariable(new QName("test"), "baz"); + + List<Node> result1 = xpath.evaluate(_dom); + assertEquals(1, result1.size()); + assertSame(_child1, result1.get(0)); + + xpath.bindVariable("test", "bargle"); + + List<Node> result2 = xpath.evaluate(_dom); + assertEquals(1, result2.size()); + assertSame(_child2, result2.get(0)); + } + + + public void testAbstractFunctions() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") + .bindNamespace("ns", NS1) + .bindFunction(new MyAbstractFunction(NS1, "myfunc")); + + assertEquals("", xpath1.evaluateAsString(_child1)); + assertEquals(NS1, xpath1.evaluateAsString(_child2)); + + XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.)") + .bindFunction(new MyAbstractFunction(NS1, "myfunc"), "ns"); + + assertEquals("", xpath2.evaluateAsString(_child1)); + assertEquals(NS1, xpath2.evaluateAsString(_child2)); + } + + + public void testStandardFunctions() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") + .bindFunction(new QName(NS1, "myfunc", "ns"), + new MyStandardFunction()); + + assertEquals("", xpath1.evaluateAsString(_child1)); + assertEquals(NS1, xpath1.evaluateAsString(_child2)); + + XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.,.)") + .bindFunction(new QName(NS2, "myfunc", "ns"), + new MyStandardFunction(), + 2); + + assertEquals("", xpath2.evaluateAsString(_child1)); + assertEquals(NS1, xpath2.evaluateAsString(_child2)); + } + + + public void testUnresolvableFunction() throws Exception + { + // we call with two arguments, it only gets resolved for one + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.,.)") + .bindFunction(new QName(NS2, "myfunc", "ns"), + new MyStandardFunction(), + 1); + try + { + xpath1.evaluateAsString(_child1); + fail("didn't throw even though arity was wrong"); + } + catch (XmlException ee) + { + // success + } + } + + + + public void testEqualsAndHashCode() throws Exception + { + Object obj1a = new XPathWrapper("//foo"); + Object obj1b = new XPathWrapper("//foo"); + Object obj2a = new XPathWrapper("//foo") + .bindDefaultNamespace("zippy"); + Object obj2b = new XPathWrapper("//foo") + .bindDefaultNamespace("zippy"); + Object obj3a = new XPathWrapper("//foo") + .bindNamespace("argle", "bargle"); + Object obj3b = new XPathWrapper("//foo") + .bindNamespace("argle", "bargle"); + Object obj4a = new XPathWrapper("//foo") + .bindVariable("argle", "bargle"); + Object obj4b = new XPathWrapper("//foo") + .bindVariable("argle", "bargle"); + Object obj5a = new XPathWrapper("//foo") + .bindFunction(new QName("foo"), null); + Object obj5b = new XPathWrapper("//foo") + .bindFunction(new QName("foo"), null); + + assertFalse(obj1a.equals(null)); + assertFalse(obj1a.equals(new Object())); + + assertTrue(obj1a.equals(obj1b)); + assertTrue(obj1b.equals(obj1a)); + assertEquals(obj1a.hashCode(), obj1b.hashCode()); + + assertFalse(obj1a.equals(obj2a)); + assertTrue(obj2a.equals(obj2b)); + assertTrue(obj2b.equals(obj2a)); + assertEquals(obj1a.hashCode(), obj2a.hashCode()); + assertEquals(obj2a.hashCode(), obj2b.hashCode()); + + assertFalse(obj1a.equals(obj3a)); + assertTrue(obj3a.equals(obj3b)); + assertTrue(obj3b.equals(obj3a)); + assertEquals(obj1a.hashCode(), obj3a.hashCode()); + assertEquals(obj3a.hashCode(), obj3b.hashCode()); + + assertFalse(obj1a.equals(obj4a)); + assertTrue(obj4a.equals(obj4b)); + assertTrue(obj4b.equals(obj4a)); + assertEquals(obj1a.hashCode(), obj4a.hashCode()); + assertEquals(obj4a.hashCode(), obj4b.hashCode()); + + assertFalse(obj1a.equals(obj5a)); + assertTrue(obj5a.equals(obj5b)); + assertTrue(obj5b.equals(obj5a)); + assertEquals(obj1a.hashCode(), obj5a.hashCode()); + assertEquals(obj5a.hashCode(), obj5b.hashCode()); + } + + + public void testToString() throws Exception + { + final String expr = "//foo"; + assertEquals(expr, new XPathWrapper(expr).toString()); + assertEquals(expr, new XPathWrapper(expr).bindNamespace("foo", "bar").toString()); + } + + + public void testFailures() throws Exception + { + try + { + new XPathWrapper(".foo.").evaluate(_dom); + fail("compiled invalid expression"); + } + catch (XmlException ee) + { + // success + } + + try + { + new XPathWrapper("@foo=$bar").evaluate(_dom); + fail("evaluated expression with unbound variable"); + } + catch (XmlException ee) + { + // success + } + } + +} Modified: trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java 2008-12-29 00:42:26 UTC (rev 64) @@ -10,7 +10,7 @@ import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; public class TestLowercase Modified: trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java 2008-12-29 00:35:16 UTC (rev 63) +++ trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java 2008-12-29 00:42:26 UTC (rev 64) @@ -10,7 +10,7 @@ import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; -import net.sf.practicalxml.XPathWrapper; +import net.sf.practicalxml.xpath.XPathWrapper; public class TestUppercase This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-29 00:35:20
|
Revision: 63 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=63&view=rev Author: kdgregory Date: 2008-12-29 00:35:16 +0000 (Mon, 29 Dec 2008) Log Message: ----------- SchemaUtil: combineSchema() renamed, now returns Document[] not Schema 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 15:56:10 UTC (rev 62) +++ trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2008-12-29 00:35:16 UTC (rev 63) @@ -94,12 +94,33 @@ */ public static Schema newSchema(SchemaFactory factory, InputSource... sources) { - Document[] parsed = parseSources(sources); + return newSchema(factory, parseSources(sources)); + } + + + /** + * Compiles one or more DOM documents 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 documents 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. + * + * @throws IllegalArgumentException if invoked without any sources. + * @throws XmlException if unable to compile the schema. + */ + public static Schema newSchema(SchemaFactory factory, Document... sources) + { try { synchronized (factory) { - return factory.newSchema(toDOMSources(parsed)); + return factory.newSchema(toDOMSources(sources)); } } catch (SAXException e) @@ -110,13 +131,14 @@ /** - * 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. + * Parses and combines one or more input sources to produce an array + * of DOM documents containing schema components. Will properly order + * the output documents based on dependencies, remove any location + * references from imports that are satisfied by the sources, and + * provides a workaround for the + * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6198705"> + * JDK 1.5 bug</a> that prevents combination of source documents with + * the same namespace. * <p> * When combining schema documents with the same namespace, all top-level * attributes (eg, <code>elementFormDefault</code>) come from the first @@ -129,9 +151,6 @@ * 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. Note that these are * <code>org.xml.sax.InputSource</code> objects for * consistency with other classes in this library; @@ -141,23 +160,12 @@ * @throws IllegalArgumentException if invoked without any sources, or if * 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). + * @throws XmlException on any parse error, or if the sources do not + * appear to be valid XSD documents. */ - public static Schema combineSchema(SchemaFactory factory, InputSource... sources) + public static Document[] combineSchemas(InputSource... sources) { - SchemaManager manager = new SchemaManager(parseSources(sources)); - try - { - synchronized (factory) - { - return factory.newSchema(manager.toDOMSources()); - } - } - catch (SAXException e) - { - throw new XmlException("unable to generate schema", e); - } + return new SchemaManager(parseSources(sources)).buildOutput(); } @@ -245,7 +253,7 @@ /** * Returns the ordered set of sources for this manager. */ - public DOMSource[] toDOMSources() + public Document[] buildOutput() { TreeSet<Document> ordered = new TreeSet<Document>(new SchemaComparator()); for (Document doc : _documents.values()) @@ -253,7 +261,7 @@ ordered.add(rebuildImports(doc)); } - return SchemaUtil.toDOMSources(ordered.toArray(new Document[ordered.size()])); + return ordered.toArray(new Document[ordered.size()]); } /** Modified: trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java 2008-12-28 15:56:10 UTC (rev 62) +++ trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java 2008-12-29 00:35:16 UTC (rev 63) @@ -208,11 +208,12 @@ + "<bargle>test</bargle>" + "</foo>"; - Schema schema = SchemaUtil.combineSchema( + Schema schema = SchemaUtil.newSchema( SchemaUtil.newFactory(new ExceptionErrorHandler()), - new InputSource(new StringReader(xsd1)), - new InputSource(new StringReader(xsd2)), - new InputSource(new StringReader(xsd3))); + SchemaUtil.combineSchemas( + new InputSource(new StringReader(xsd1)), + new InputSource(new StringReader(xsd2)), + new InputSource(new StringReader(xsd3)))); assertNotNull(schema); ParseUtil.validatingParse(new InputSource(new StringReader(xml)), @@ -245,10 +246,12 @@ + "<argle>12</argle>" + "</foo>"; - Schema schema = SchemaUtil.combineSchema( + + Schema schema = SchemaUtil.newSchema( SchemaUtil.newFactory(new ExceptionErrorHandler()), - new InputSource(new StringReader(xsd1)), - new InputSource(new StringReader(xsd2))); + SchemaUtil.combineSchemas( + new InputSource(new StringReader(xsd1)), + new InputSource(new StringReader(xsd2)))); assertNotNull(schema); ParseUtil.validatingParse(new InputSource(new StringReader(xml)), @@ -296,11 +299,13 @@ + "</foo>"; // note: these sources are intentionally out-of-order - Schema schema = SchemaUtil.combineSchema( + + Schema schema = SchemaUtil.newSchema( SchemaUtil.newFactory(new ExceptionErrorHandler()), - new InputSource(new StringReader(xsd1)), - new InputSource(new StringReader(xsd2)), - new InputSource(new StringReader(xsd3))); + SchemaUtil.combineSchemas( + new InputSource(new StringReader(xsd1)), + new InputSource(new StringReader(xsd2)), + new InputSource(new StringReader(xsd3)))); assertNotNull(schema); ParseUtil.validatingParse(new InputSource(new StringReader(xml)), This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
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. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-28 00:46:56
|
Revision: 61 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=61&view=rev Author: kdgregory Date: 2008-12-28 00:46:35 +0000 (Sun, 28 Dec 2008) Log Message: ----------- OutputUtil: add elementToString(), treeToString() Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/OutputUtil.java trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java Modified: trunk/src/main/java/net/sf/practicalxml/OutputUtil.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/OutputUtil.java 2008-12-24 00:31:26 UTC (rev 60) +++ trunk/src/main/java/net/sf/practicalxml/OutputUtil.java 2008-12-28 00:46:35 UTC (rev 61) @@ -14,6 +14,7 @@ import javax.xml.transform.stream.StreamResult; import org.w3c.dom.Document; +import org.w3c.dom.Element; /** @@ -25,6 +26,31 @@ public class OutputUtil { /** + * A simple <code>toString()</code> for an element, using the format + * "<code>{<i>NSURI</i>}<i>LOCALNAME</i></code>"; if the element has no + * namespace, the brackets remain but are empty. + */ + public static String elementToString(Element elem) + { + return appendElementString(new StringBuilder(256), elem).toString(); + } + + + /** + * Debug dump of the tree rooted at the specified element. Each line holds + * one element, + * + * + * @param elem + * @param indent + */ + public static String treeToString(Element elem, int indent) + { + return appendTreeString(new StringBuilder(1024), elem, indent, 0).toString(); + } + + + /** * Writes a DOM document to a simple string format, without a prologue or * whitespace between elements. * <p> @@ -206,6 +232,41 @@ //---------------------------------------------------------------------------- /** + * The actual implementation of {@link #elementToString}, which appends + * the string format to a passed buffer. Returns the buffer as a + * convenience. + */ + private static StringBuilder appendElementString(StringBuilder buf, Element elem) + { + String namespaceURI = elem.getNamespaceURI(); + String localName = DomUtil.getLocalName(elem); + + return buf.append("{") + .append((namespaceURI != null) ? namespaceURI : "") + .append("}") + .append(localName); + } + + /** + * Actual implementation of <code>dumpTree</code>, using a passed buffer + * so that we're not doing lots of string concats + */ + private static StringBuilder appendTreeString(StringBuilder buf, Element elem, int indent, int curIndent) + { + if (buf.length() > 0) + buf.append("\n"); + for (int ii = 0 ; ii < curIndent ; ii++) + buf.append(" "); + appendElementString(buf, elem); + for (Element child : DomUtil.getChildren(elem)) + { + appendTreeString(buf, child, indent, curIndent + indent); + } + return buf; + } + + + /** * Flushes an <code>OutputStream</code>, wrapping exceptions. */ private static void flushStream(OutputStream stream) Modified: trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java 2008-12-24 00:31:26 UTC (rev 60) +++ trunk/src/test/java/net/sf/practicalxml/TestOutputUtil.java 2008-12-28 00:46:35 UTC (rev 61) @@ -31,10 +31,30 @@ //---------------------------------------------------------------------------- -// Test Cases -- we're looking for overall structure, assume that the output -// transform will do the right thing with the details +// Test Cases -- in most of these tests we look for overall structure, assume +// that the output transform will do the right thing //---------------------------------------------------------------------------- + public void testElementToString() throws Exception + { + Element root = DomUtil.newDocument("foo"); + Element child1 = DomUtil.appendChild(root, "argle", "bargle"); + + assertEquals("{}foo", OutputUtil.elementToString(root)); + assertEquals("{argle}bargle", OutputUtil.elementToString(child1)); + } + + + public void testTreeToString() throws Exception + { + Element root = DomUtil.newDocument("foo"); + Element child1 = DomUtil.appendChild(root, "argle", "bargle"); + + assertEquals("{}foo\n {argle}bargle", OutputUtil.treeToString(root, 2)); + assertEquals("{argle}bargle", OutputUtil.treeToString(child1, 2)); + } + + public void testCompactStringSingleElement() throws Exception { Element root = DomUtil.newDocument(EL_ROOT); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-24 00:33:04
|
Revision: 60 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=60&view=rev Author: kdgregory Date: 2008-12-24 00:31:26 +0000 (Wed, 24 Dec 2008) Log Message: ----------- SchemaUtil.newSchema(): bug 2417477 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-20 12:35:23 UTC (rev 59) +++ trunk/src/main/java/net/sf/practicalxml/SchemaUtil.java 2008-12-24 00:31:26 UTC (rev 60) @@ -4,28 +4,69 @@ import javax.xml.transform.dom.DOMSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; - -import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.SAXException; +import org.apache.commons.lang.StringUtils; + /** * A collection of static utility methods for working with XML Schema * 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 SCHEMA_PREFIX = "xsd"; - private final static String SCHEMA_ROOT = "schema"; + private final static String EL_SCHEMA = "schema"; + private final static String ATTR_TARGET_NS = "targetNamespace"; /** @@ -42,44 +83,34 @@ /** - * A workaround for Sun bug <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6198705"> - * 6198705</code>, also known as Xerces bug <a href="http://issues.apache.org/jira/browse/XERCESJ-1130"> - * XERCESJ-1130</code>, which prevents the <code>SchemaFactory.ewSchema</code> - * method from combining multiple sources that have the same namespace. - * <p> - * This method works by parsing the children of the passed sources, - * verifying that their root element is <code><xsd:schema></code>, - * combining the children of this root into a new document, and calling - * <code>newSchema()</code> on the result. + * 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. * * @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. * - * @throws XmlException on any failure that is not handled by the - * factory's <code>ErrorHandler</code>. This includes parse - * errors and local validation of the source root element. + * @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). */ public static Schema newSchema(SchemaFactory factory, InputSource... sources) { - Element combined = DomUtil.newDocument(SCHEMA_NS, SCHEMA_PREFIX + ":" + SCHEMA_ROOT); - for (InputSource source : 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++) { - combineSchema(combined, source); + combine(dom, parse(sources[ii])); } - - try - { - synchronized (factory) - { - return factory.newSchema(new DOMSource(combined.getOwnerDocument())); - } - } - catch (SAXException e) - { - throw new XmlException("unable to generate schema", e); - } + return generate(factory, dom); } @@ -88,31 +119,65 @@ //---------------------------------------------------------------------------- /** - * Parses, verifies, and combines a source document. + * 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" */ - private static void combineSchema(Element newRoot, InputSource source) + private static Document parse(InputSource source) { - Document doc = ParseUtil.parse(source); - NamedNodeMap attributeMap = doc.getDocumentElement().getAttributes(); - if (attributeMap != null) + Document dom = ParseUtil.parse(source); + Element root = dom.getDocumentElement(); + if (!DomUtil.isNamed(root, SCHEMA_NS, EL_SCHEMA)) { - for (int i=0; i<attributeMap.getLength(); i++) - { - Attr attribute = (Attr) attributeMap.item(i); - newRoot.setAttribute(attribute.getName(), attribute.getValue()); - } - } - Element root = doc.getDocumentElement(); - if (!DomUtil.isNamed(root, SCHEMA_NS, SCHEMA_ROOT)) - { throw new XmlException("invalid root element name: {" + root.getNamespaceURI() + "}" + DomUtil.getLocalName(root)); } - for (Element child : DomUtil.getChildren(root)) + return dom; + } + + + /** + * 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. + */ + private static void combine(Document dst, Document src) + { + Element srcRoot = src.getDocumentElement(); + Element dstRoot = dst.getDocumentElement(); + + 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 + "\""); + + for (Element child : DomUtil.getChildren(srcRoot)) { - Node tmp = newRoot.getOwnerDocument().importNode(child, true); - newRoot.appendChild(tmp); + Node tmp = dst.importNode(child, true); + dstRoot.appendChild(tmp); } } + + + /** + * Generates a new <code>Schema</code> object from a DOM document. + */ + private static Schema generate(SchemaFactory factory, Document dom) + { + try + { + synchronized (factory) + { + return factory.newSchema(new DOMSource(dom)); + } + } + 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-20 12:35:23 UTC (rev 59) +++ trunk/src/test/java/net/sf/practicalxml/TestSchemaUtil.java 2008-12-24 00:31:26 UTC (rev 60) @@ -1,6 +1,5 @@ package net.sf.practicalxml; -import java.io.Reader; import java.io.StringReader; import javax.xml.validation.Schema; @@ -16,47 +15,19 @@ extends AbstractTestCase { //---------------------------------------------------------------------------- -// Definitions for newSchema() testing +// Support Code //---------------------------------------------------------------------------- - public final static String NEW_SCHEMA_START - = "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\">"; - public final static String NEW_SCHEMA_DEF_1 - = "<xsd:element name=\"foo\" type=\"FooType\"/>"; - public final static String NEW_SCHEMA_DEF_2 - = "<xsd:complexType name=\"FooType\">" - + "<xsd:sequence>" - + "<xsd:element name=\"argle\" type=\"xsd:integer\"/>" - + "<xsd:element name=\"bargle\" type=\"BarType\"/>" - + "</xsd:sequence>" - + "</xsd:complexType>"; - - public final static String NEW_SCHEMA_DEF_3 - = "<xsd:simpleType name=\"BarType\">" - + "<xsd:restriction base=\"xsd:string\">" - + "</xsd:restriction>" - + "</xsd:simpleType>"; - - public final static String NEW_SCHEMA_FINISH - = "</xsd:schema>"; - - public final static String NEW_SCHEMA_WITH_NAMESPACE_START - = "<xsd:schema xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" targetNamespace=\"http://foo/bar\" xmlns:bar=\"http://foo/bar\" elementFormDefault=\"qualified\">"; - - public final static String NEW_SCHEMA_WITH_NAMESPACE_DEF_1 - = "<xsd:element name=\"foo\" type=\"bar:FooType\"/>"; - - public final static String NEW_SCHEMA_WITH_NAMESPACE_DEF_2 - = "<xsd:complexType name=\"FooType\">" - + "<xsd:sequence>" - + "<xsd:element name=\"argle\" type=\"xsd:integer\"/>" - + "</xsd:sequence>" - + "</xsd:complexType>"; - //---------------------------------------------------------------------------- // Test Cases +// +// Note: since we can't examine a schema once it's compiled, we need to +// verify newSchema() operation with actual instance docs ... and +// to make more sense of this, each test case should define its +// schema and instance docs inline ... yes, that's a lot of repeated +// XML, but I think it will make verification easier //---------------------------------------------------------------------------- public void testNewFactory() throws Exception @@ -69,47 +40,173 @@ } - public void testNewSchemaWithSingleSource() throws Exception + public void testNewSchemaSingleSource() throws Exception { - Reader xsd = new StringReader( - NEW_SCHEMA_START - + NEW_SCHEMA_DEF_1 + NEW_SCHEMA_DEF_2 + NEW_SCHEMA_DEF_3 - + NEW_SCHEMA_FINISH); + String xsd = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" + + "<xsd:element name='foo' type='FooType'/>" + + "<xsd:complexType name='FooType'>" + + "<xsd:sequence>" + + "<xsd:element name='argle' type='xsd:integer'/>" + + "<xsd:element name='bargle' type='BarType'/>" + + "</xsd:sequence>" + + "</xsd:complexType>" + + "<xsd:simpleType name='BarType'>" + + "<xsd:restriction base='xsd:string'>" + + "</xsd:restriction>" + + "</xsd:simpleType>" + + "</xsd:schema>"; + String xml = "<foo>" + + "<argle>12</argle>" + + "<bargle>test</bargle>" + + "</foo>"; + Schema schema = SchemaUtil.newSchema( SchemaUtil.newFactory(new ExceptionErrorHandler()), - new InputSource(xsd)); + new InputSource(new StringReader(xsd))); + assertNotNull(schema); + ParseUtil.validatingParse(new InputSource(new StringReader(xml)), + schema, + new ExceptionErrorHandler()); } - public void testNewSchemaWithMultipleSources() throws Exception + public void testNewSchemaMultipleSources() throws Exception { - Reader xsd1 = new StringReader( - NEW_SCHEMA_START + NEW_SCHEMA_DEF_1 + NEW_SCHEMA_FINISH); - Reader xsd2 = new StringReader( - NEW_SCHEMA_START + NEW_SCHEMA_DEF_2 + NEW_SCHEMA_FINISH); - Reader xsd3 = new StringReader( - NEW_SCHEMA_START + NEW_SCHEMA_DEF_3 + NEW_SCHEMA_FINISH); + String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" + + "<xsd:element name='foo' type='FooType'/>" + + "</xsd:schema>"; + String xsd2 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" + + "<xsd:complexType name='FooType'>" + + "<xsd:sequence>" + + "<xsd:element name='argle' type='xsd:integer'/>" + + "<xsd:element name='bargle' type='BarType'/>" + + "</xsd:sequence>" + + "</xsd:complexType>" + + "</xsd:schema>"; + String xsd3 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'>" + + "<xsd:simpleType name='BarType'>" + + "<xsd:restriction base='xsd:string'>" + + "</xsd:restriction>" + + "</xsd:simpleType>" + + "</xsd:schema>"; + String xml = "<foo>" + + "<argle>12</argle>" + + "<bargle>test</bargle>" + + "</foo>"; + Schema schema = SchemaUtil.newSchema( SchemaUtil.newFactory(new ExceptionErrorHandler()), - new InputSource(xsd1), - new InputSource(xsd2), - new InputSource(xsd3)); + new InputSource(new StringReader(xsd1)), + new InputSource(new StringReader(xsd2)), + new InputSource(new StringReader(xsd3))); + assertNotNull(schema); + ParseUtil.validatingParse(new InputSource(new StringReader(xml)), + schema, + new ExceptionErrorHandler()); } - - public void testNewSchemaWithNamespace() throws Exception + + + public void testNewSchemaSingleSourceWithNamespace() throws Exception { - Reader xsd = new StringReader( - NEW_SCHEMA_WITH_NAMESPACE_START - + NEW_SCHEMA_WITH_NAMESPACE_DEF_1 + NEW_SCHEMA_WITH_NAMESPACE_DEF_2 - + NEW_SCHEMA_FINISH); + 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 xml = "<foo xmlns='http://foo.example.com'>" + + "<argle>12</argle>" + + "</foo>"; + Schema schema = SchemaUtil.newSchema( SchemaUtil.newFactory(new ExceptionErrorHandler()), - new InputSource(xsd)); + new InputSource(new StringReader(xsd))); + assertNotNull(schema); + ParseUtil.validatingParse(new InputSource(new StringReader(xml)), + schema, + new ExceptionErrorHandler()); } + + + public void testNewSchemaMultipleSourceWithNamespace() throws Exception + { + 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( + SchemaUtil.newFactory(new ExceptionErrorHandler()), + new InputSource(new StringReader(xsd1)), + new InputSource(new StringReader(xsd2))); + + assertNotNull(schema); + ParseUtil.validatingParse(new InputSource(new StringReader(xml)), + schema, + new ExceptionErrorHandler()); + } + + + public void testNewSchemaFailMultipleNamepaces() throws Exception + { + String xsd1 = "<xsd:schema xmlns:xsd='http://www.w3.org/2001/XMLSchema'" + + " xmlns:foo='http://foo.example.com'" + + " 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'" + + " targetNamespace='http://bar.example.com'" + + " elementFormDefault='qualified'" + + ">" + + "<xsd:complexType name='FooType'>" + + "<xsd:sequence>" + + "<xsd:element name='argle' type='xsd:integer'/>" + + "</xsd:sequence>" + + "</xsd:complexType>" + + "</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 + } + } } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-20 12:35:26
|
Revision: 59 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=59&view=rev Author: kdgregory Date: 2008-12-20 12:35:23 +0000 (Sat, 20 Dec 2008) Log Message: ----------- Add in-situ tests for Uppercase and Lowercase Modified Paths: -------------- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java Modified: trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java 2008-12-20 11:29:17 UTC (rev 58) +++ trunk/src/test/java/net/sf/practicalxml/xpath/function/TestLowercase.java 2008-12-20 12:35:23 UTC (rev 59) @@ -2,18 +2,53 @@ import java.util.Arrays; import java.util.Collections; +import java.util.List; import javax.xml.xpath.XPathFunctionException; - import org.w3c.dom.Element; +import org.w3c.dom.Node; import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XPathWrapper; public class TestLowercase extends AbstractTestCase { + public TestLowercase(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Setup +//---------------------------------------------------------------------------- + + private Element _root; + private Element _child1; + private Element _child2; + private Element _child3; + + @Override + protected void setUp() throws Exception + { + _root = DomUtil.newDocument("foo"); + _child1 = DomUtil.appendChild(_root, "bar"); + _child2 = DomUtil.appendChild(_root, "baz"); + _child3 = DomUtil.appendChild(_root, "foo2"); + + DomUtil.setText(_root, "Test"); + DomUtil.setText(_child1, "Test1"); + DomUtil.setText(_child2, "Test2"); + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + public void testConstruction() throws Exception { Lowercase fn = new Lowercase(); @@ -23,6 +58,7 @@ assertEquals(1, fn.getMaxArgCount()); } + public void testLiteralString() throws Exception { assertEquals( @@ -33,26 +69,17 @@ public void testNodeList() throws Exception { - Element root = DomUtil.newDocument("foo"); - Element child1 = DomUtil.appendChild(root, "bar"); - Element child2 = DomUtil.appendChild(root, "baz"); - DomUtil.setText(root, "Test"); - DomUtil.setText(child1, "Test2"); - DomUtil.setText(child2, "Test3"); - assertEquals( - "test2", - new Lowercase().evaluate(Arrays.asList(root.getChildNodes()))); + "test1", + new Lowercase().evaluate(Arrays.asList(_root.getChildNodes()))); } public void testEmptyNodeList() throws Exception { - Element root = DomUtil.newDocument("foo"); - assertEquals( "", - new Lowercase().evaluate(Arrays.asList(root.getChildNodes()))); + new Lowercase().evaluate(Arrays.asList(_child3.getChildNodes()))); } @@ -77,4 +104,16 @@ } } + + public void testInSitu() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("//*[ns:lowercase(text()) = 'test1']") + .bindFunction(new Lowercase(), "ns"); + List<Node> result1 = xpath1.evaluate(_root); + assertSame(_child1, result1.get(0)); + + XPathWrapper xpath2 = new XPathWrapper("//*[ns:lowercase(text()) = 'TEST1']") + .bindFunction(new Lowercase(), "ns"); + assertEquals(0, xpath2.evaluate(_root).size()); + } } Modified: trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java 2008-12-20 11:29:17 UTC (rev 58) +++ trunk/src/test/java/net/sf/practicalxml/xpath/function/TestUppercase.java 2008-12-20 12:35:23 UTC (rev 59) @@ -2,18 +2,53 @@ import java.util.Arrays; import java.util.Collections; +import java.util.List; import javax.xml.xpath.XPathFunctionException; - import org.w3c.dom.Element; +import org.w3c.dom.Node; import net.sf.practicalxml.AbstractTestCase; import net.sf.practicalxml.DomUtil; +import net.sf.practicalxml.XPathWrapper; public class TestUppercase extends AbstractTestCase { + public TestUppercase(String name) + { + super(name); + } + + +//---------------------------------------------------------------------------- +// Setup +//---------------------------------------------------------------------------- + + private Element _root; + private Element _child1; + private Element _child2; + private Element _child3; + + @Override + protected void setUp() throws Exception + { + _root = DomUtil.newDocument("foo"); + _child1 = DomUtil.appendChild(_root, "bar"); + _child2 = DomUtil.appendChild(_root, "baz"); + _child3 = DomUtil.appendChild(_root, "foo2"); + + DomUtil.setText(_root, "Test"); + DomUtil.setText(_child1, "Test1"); + DomUtil.setText(_child2, "Test2"); + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + public void testConstruction() throws Exception { Uppercase fn = new Uppercase(); @@ -23,6 +58,7 @@ assertEquals(1, fn.getMaxArgCount()); } + public void testLiteralString() throws Exception { assertEquals( @@ -33,26 +69,17 @@ public void testNodeList() throws Exception { - Element root = DomUtil.newDocument("foo"); - Element child1 = DomUtil.appendChild(root, "bar"); - Element child2 = DomUtil.appendChild(root, "baz"); - DomUtil.setText(root, "Test"); - DomUtil.setText(child1, "Test2"); - DomUtil.setText(child2, "Test3"); - assertEquals( - "TEST2", - new Uppercase().evaluate(Arrays.asList(root.getChildNodes()))); + "TEST1", + new Uppercase().evaluate(Arrays.asList(_root.getChildNodes()))); } public void testEmptyNodeList() throws Exception { - Element root = DomUtil.newDocument("foo"); - assertEquals( "", - new Lowercase().evaluate(Arrays.asList(root.getChildNodes()))); + new Uppercase().evaluate(Arrays.asList(_child3.getChildNodes()))); } @@ -77,4 +104,17 @@ } } + + public void testInSitu() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("//*[ns:uppercase(text()) = 'TEST1']") + .bindFunction(new Uppercase(), "ns"); + List<Node> result1 = xpath1.evaluate(_root); + assertSame(_child1, result1.get(0)); + + XPathWrapper xpath2 = new XPathWrapper("//*[ns:uppercase(text()) = 'test1']") + .bindFunction(new Uppercase(), "ns"); + assertEquals(0, xpath2.evaluate(_root).size()); + } + } This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |
From: Auto-Generated S. C. M. <pra...@li...> - 2008-12-20 11:29:20
|
Revision: 58 http://practicalxml.svn.sourceforge.net/practicalxml/?rev=58&view=rev Author: kdgregory Date: 2008-12-20 11:29:17 +0000 (Sat, 20 Dec 2008) Log Message: ----------- add FunctionResolver update XPathWrapper to use it Modified Paths: -------------- trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java Added Paths: ----------- trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java trunk/src/test/java/net/sf/practicalxml/xpath/TestFunctionResolver.java Modified: trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java 2008-12-17 14:03:16 UTC (rev 57) +++ trunk/src/main/java/net/sf/practicalxml/XPathWrapper.java 2008-12-20 11:29:17 UTC (rev 58) @@ -11,7 +11,6 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; import javax.xml.xpath.XPathFunction; -import javax.xml.xpath.XPathFunctionResolver; import javax.xml.xpath.XPathVariableResolver; import org.w3c.dom.Document; @@ -19,6 +18,8 @@ import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import net.sf.practicalxml.xpath.AbstractFunction; +import net.sf.practicalxml.xpath.FunctionResolver; import net.sf.practicalxml.xpath.NamespaceResolver; @@ -34,7 +35,7 @@ private final String _expr; private final NamespaceResolver _nsResolver = new NamespaceResolver(); private Map<QName,Object> _variables = new HashMap<QName,Object>(); - private Map<QName,XPathFunction> _functions = new HashMap<QName,XPathFunction>(); + private FunctionResolver _functions = new FunctionResolver(); private XPathExpression _compiled; @@ -129,22 +130,127 @@ /** - * Binds a function to this expression, replacing any previous binding - * for the specified name. Note that <code>XPathFunctionResolver</code> - * can support multiple functions with the same name; we don't. + * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this + * expression. Subsequent calls to this method with the same name will + * silently replace the binding. * <p> * Per the JDK documentation, user-defined functions must occupy their - * own namespace. You will need to add a namespace binding along with - * your function binding. + * own namespace. You must bind a namespace for this function, and use + * the bound prefix to reference it in your expression. Alternatively, + * you can call the variant of <code>bindFunction()</code> that binds + * a prefix to the function's namespace. * - * @param name The fully-qualified name for this binding. + * @param func The function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(AbstractFunction func) + { + _functions.addFunction(func); + return this; + } + + + /** + * Binds an {@link net.sf.practicalxml.xpath.AbstractFunction} to this + * expression, along with the prefix used to access that function. + * Subsequent calls to this method with the same name will silently + * replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. This method will retrieve the namespace from the + * function, and bind it to the passed prefix. + * + * @param func The function. + * @param prefix The prefix to bind to this function's namespace. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(AbstractFunction func, String prefix) + { + _functions.addFunction(func); + return bindNamespace(prefix, func.getNamespaceUri()); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling any number of arguments. Subsequent calls to this method + * with the same name will silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. * @param func The function to bind to this name. * * @return The wrapper, so that calls may be chained. */ public XPathWrapper bindFunction(QName name, XPathFunction func) { - _functions.put(name, func); + return bindFunction(name, func, 0, Integer.MAX_VALUE); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling a specific number of arguments. Subsequent calls to this + * method with the same name and arity will silently replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * @param arity The number of arguments accepted by this function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func, int arity) + { + return bindFunction(name, func, arity, arity); + } + + + /** + * Binds a standard <code>XPathFunction</code> to this expression, + * handling a specific range of arguments. Subsequent calls to this + * method with the same name and range of arguments will silently + * replace the binding. + * <p> + * Per the JDK documentation, user-defined functions must occupy their + * own namespace. If the qualified name that you pass to this method + * includes a prefix, the associated namespace will be bound to that + * prefix. If not, you must bind the namespace explicitly. In either + * case, you must refer to the function in your expression using a + * bound prefix. + * + * @param name The qualified name for this function. Must contain + * a name and namespace, may contain a prefix. + * @param func The function to bind to this name. + * @param minArity The minimum number of arguments accepted by this + * function. + * @param maxArity The maximum number of arguments accepted by this + * function. + * + * @return The wrapper, so that calls may be chained. + */ + public XPathWrapper bindFunction(QName name, XPathFunction func, + int minArity, int maxArity) + { + _functions.addFunction(func, name, minArity, maxArity); + if (!"".equals(name.getPrefix())) + bindNamespace(name.getPrefix(), name.getNamespaceURI()); return this; } @@ -335,7 +441,7 @@ XPath xpath = XPathFactory.newInstance().newXPath(); xpath.setNamespaceContext(_nsResolver); xpath.setXPathVariableResolver(new MyVariableResolver()); - xpath.setXPathFunctionResolver(new MyFunctionResolver()); + xpath.setXPathFunctionResolver(_functions); _compiled = xpath.compile(_expr); } catch (XPathExpressionException ee) @@ -356,18 +462,4 @@ return _variables.get(name); } } - - - /** - * Resolver for function references. Note that we ignore the "arity" - * argument, meaning that there's a single binding per name. - */ - private class MyFunctionResolver - implements XPathFunctionResolver - { - public XPathFunction resolveFunction(QName name, int arity) - { - return _functions.get(name); - } - } } Modified: trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java 2008-12-17 14:03:16 UTC (rev 57) +++ trunk/src/main/java/net/sf/practicalxml/xpath/AbstractFunction.java 2008-12-20 11:29:17 UTC (rev 58) @@ -67,9 +67,7 @@ */ protected AbstractFunction(String nsUri, String localName) { - _qname = new QName(nsUri, localName); - _minArgCount = 0; - _maxArgCount = Integer.MAX_VALUE; + this(nsUri, localName, 0, Integer.MAX_VALUE); } @@ -78,9 +76,7 @@ */ protected AbstractFunction(String nsUri, String localName, int numArgs) { - _qname = new QName(nsUri, localName); - _minArgCount = numArgs; - _maxArgCount = numArgs; + this(nsUri, localName, numArgs, numArgs); } @@ -89,7 +85,16 @@ */ protected AbstractFunction(String nsUri, String localName, int minArgs, int maxArgs) { - _qname = new QName(nsUri, localName); + this(new QName(nsUri, localName), minArgs, maxArgs); + } + + + /** + * Base constructor. + */ + protected AbstractFunction(QName qname, int minArgs, int maxArgs) + { + _qname = qname; _minArgCount = minArgs; _maxArgCount = maxArgs; } @@ -152,12 +157,25 @@ public boolean isMatch(QName qname, int arity) { return _qname.equals(qname) - && (arity >= _minArgCount) - && (arity <= _maxArgCount); + && (arity >= _minArgCount) + && (arity <= _maxArgCount); } + + /** + * Determines whether this function can handle the specified number of + * arguments. This is used by {@link FunctionResolver}, which already + * knows that the QName matches. + */ + public boolean isArityMatch(int arity) + { + return (arity >= _minArgCount) + && (arity <= _maxArgCount); + } + + //---------------------------------------------------------------------------- -// Implementation of Comparable +// Implementation of Comparable, Object overrides //---------------------------------------------------------------------------- /** @@ -197,6 +215,40 @@ } + /** + * Two instances are considered equal if they have the same qualified + * name and accept the same range of arguments. This supports use with + * the collections framework, which is the only place that you should + * be checking equality. + */ + @Override + public final boolean equals(Object obj) + { + if (this == obj) + return true; + else if (obj instanceof AbstractFunction<?>) + { + AbstractFunction<?> that = (AbstractFunction<?>)obj; + return this._qname.equals(that._qname) + && this._minArgCount == that._minArgCount + && this._maxArgCount == that._maxArgCount; + } + else + return false; + } + + + /** + * Hashcode is based on the qualified name, does not consider argument + * count. + */ + @Override + public final int hashCode() + { + return _qname.hashCode(); + } + + //---------------------------------------------------------------------------- // Implementation of XPathFunction //---------------------------------------------------------------------------- @@ -376,6 +428,22 @@ * Processes a <code>null</code> argument — it's unclear whether * this can ever happen. The default implementation throws <code> * IllegalArgumentException</code>. + + + @Override + public boolean equals(Object obj) + { + // FIXME - implement + return super.equals(obj); + } + + + @Override + public int hashCode() + { + // FIXME - implement + return super.hashCode(); + } * * @param index Index of the argument, numbered from 0. * @param helper Helper object to preserve intermediate results. Added: trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java =================================================================== --- trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java (rev 0) +++ trunk/src/main/java/net/sf/practicalxml/xpath/FunctionResolver.java 2008-12-20 11:29:17 UTC (rev 58) @@ -0,0 +1,266 @@ +package net.sf.practicalxml.xpath; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; + +import javax.xml.namespace.QName; +import javax.xml.xpath.XPathFunction; +import javax.xml.xpath.XPathFunctionException; +import javax.xml.xpath.XPathFunctionResolver; + + +/** + * An <code>XPathFunctionResolver</code> designed for use with subclasses of + * {@link AbstractFunction} (but will work with any <code>XPathFunction</code>). + * Functions are resolved based on name, namespace, and arity. Where multiple + * eligible functions have the same name and namespace, the comparison rules of + * <code>AbstractFunction</code> are used to pick the most appropriate function: + * smaller range of accepted arguments first, followed by lower minimum argument + * count. + * <p> + * This object follows the builder pattern: <code>addFunction()</code> returns + * the object itself, so that calls may be chained. + */ +public class FunctionResolver +implements XPathFunctionResolver +{ + private Map<QName,FunctionHolder> _table = new HashMap<QName,FunctionHolder>(); + +//---------------------------------------------------------------------------- +// Public Methods +//---------------------------------------------------------------------------- + + /** + * Adds a subclass of {@link AbstractFunction} to this resolver. This + * function provides its own information about name, namespace, and + * supported number of arguments. + * <p> + * If the same function has already been added, will silently replace it. + * + * @return The resolver, so that calls may be chained. + */ + public FunctionResolver addFunction(AbstractFunction<?> func) + { + FunctionHolder holder = _table.get(func.getQName()); + if (holder == null) + { + holder = new FunctionHolder(func); + _table.put(func.getQName(), holder); + } + else + { + holder.put(func); + } + return this; + } + + + /** + * Adds a normal <code>XPathFunction</code> to this resolver, without + * specifying argument count. This function will be chosen for any number + * of arguments, provided that there is not a more-specific binding). + * <p> + * If a function has already been added with the same name and argument + * range, this call will silently replace it. + * <p> + * Note: the resolver wraps the passed function with a decorator that + * allows it to be managed. The resolver will return this decorator + * object rather than the original function. + * + * @return The resolver, so that calls may be chained. + */ + public FunctionResolver addFunction(XPathFunction func, QName name) + { + return addFunction(func, name, 0, Integer.MAX_VALUE); + } + + + /** + * Adds a normal <code>XPathFunction</code> to this resolver. Specifies + * the exact number of arguments for which this function will be chosen. + * <p> + * If a function has already been added with the same name and argument + * range, this call will silently replace it. + * <p> + * Note: the resolver wraps the passed function with a decorator that + * allows it to be managed. The resolver will return this decorator + * object rather than the original function. + * + * @return The resolver, so that calls may be chained. + */ + public FunctionResolver addFunction(XPathFunction func, QName name, int argCount) + { + return addFunction(func, name, argCount, argCount); + } + + + /** + * Adds a normal <code>XPathFunction</code> to this resolver. Specifies + * the minimum and maximum number of arguments for which this function + * will be chosen. + * <p> + * If a function has already been added with the same name and argument + * range, this call will silently replace it. + * <p> + * Note: the resolver wraps the passed function with a decorator that + * allows it to be managed. The resolver will return this decorator + * object rather than the original function. + * + * @return The resolver, so that calls may be chained. + */ + public FunctionResolver addFunction(XPathFunction func, QName name, int minArgCount, int maxArgCount) + { + return addFunction(new StandardFunctionAdapter(func, name, minArgCount, maxArgCount)); + } + + +//---------------------------------------------------------------------------- +// XPathFunctionResolver +//---------------------------------------------------------------------------- + + /** + * Picks the "most eligble" function of those bound to this resolver. + * If unable to find any function that matches, returns <code>null</code>. + */ + public XPathFunction resolveFunction(QName functionName, int arity) + { + FunctionHolder holder = _table.get(functionName); + return (holder != null) ? holder.get(arity) + : null; + } + + +//---------------------------------------------------------------------------- +// Object overrides +//---------------------------------------------------------------------------- + + /** + * Two instances are equal if they have the same number of functions, + * and each has a counterpart with the same QName and arity range (but + * not the same implementation). + */ + @Override + public final boolean equals(Object obj) + { + if (this == obj) + return true; + else if (obj instanceof FunctionResolver) + return this._table.equals(((FunctionResolver)obj)._table); + else + return false; + } + + + @Override + public final int hashCode() + { + // I suspect this is a very inefficient way to compute hashcode, + // but this object should never be stored in a hashing structure + // -- if you need to do that, come up with a better implementation + return _table.keySet().hashCode(); + } + + +//---------------------------------------------------------------------------- +// Internals +//---------------------------------------------------------------------------- + + /** + * Holder for AbstractFunction instances. Can deal with 1 or N, and + * pick the appropriate one for a particular evaluation. + */ + private static class FunctionHolder + { + private AbstractFunction<?> _onlyOne; + private TreeSet<AbstractFunction<?>> _hasMany; + + public FunctionHolder(AbstractFunction<?> initial) + { + _onlyOne = initial; + } + + public void put(AbstractFunction<?> func) + { + if (_hasMany != null) + { + // remove old implementation if it exists + _hasMany.remove(func); + _hasMany.add(func); + } + else if (_onlyOne.equals(func)) + { + _onlyOne = func; + } + else + { + _hasMany = new TreeSet<AbstractFunction<?>>(); + _hasMany.add(func); + _hasMany.add(_onlyOne); + _onlyOne = null; + } + } + + public AbstractFunction<?> get(int arity) + { + if (_onlyOne != null) + { + return (_onlyOne.isArityMatch(arity)) + ? _onlyOne + : null; + } + + for (AbstractFunction<?> func : _hasMany) + { + if (func.isArityMatch(arity)) + return func; + } + + return null; + } + + @Override + public boolean equals(Object obj) + { + // if this ever fails, someone needs to fix their code + FunctionHolder that = (FunctionHolder)obj; + + return (_onlyOne != null) + ? this._onlyOne.equals(that._onlyOne) + : this._hasMany.equals(that._hasMany); + } + + @Override + public int hashCode() + { + // I know this will never be stored as the key of a hash-table, + // but I want to keep static analysis tools quiet + return 0; + } + } + + + /** + * Adapter for standard XPathFunction instances, allowing them to be + * managed by this resolver. + */ + private static class StandardFunctionAdapter + extends AbstractFunction<Object> + { + final XPathFunction _func; + + public StandardFunctionAdapter(XPathFunction func, QName name, int minArgCount, int maxArgCount) + { + super(name, minArgCount, maxArgCount); + _func = func; + } + + @Override + public Object evaluate(List args) + throws XPathFunctionException + { + return _func.evaluate(args); + } + } +} Modified: trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java 2008-12-17 14:03:16 UTC (rev 57) +++ trunk/src/test/java/net/sf/practicalxml/TestXPathWrapper.java 2008-12-20 11:29:17 UTC (rev 58) @@ -5,6 +5,8 @@ import javax.xml.xpath.XPathFunction; import javax.xml.xpath.XPathFunctionException; +import net.sf.practicalxml.xpath.AbstractFunction; + import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; @@ -50,7 +52,42 @@ // Support Code //---------------------------------------------------------------------------- + /** + * A standard XPath function implementation that returns the namespace + * of the first selected node. + */ + private static class MyStandardFunction + implements XPathFunction + { + public Object evaluate(List args) + throws XPathFunctionException + { + NodeList arg = (NodeList)args.get(0); + return arg.item(0).getNamespaceURI(); + } + } + + /** + * An <code>AbstractFunction</code> implementation that returns the + * namespace of the first selected node. + */ + private static class MyAbstractFunction + extends AbstractFunction<String> + { + public MyAbstractFunction(String nsUri, String name) + { + super(nsUri, name); + } + + @Override + protected String processArg(int index, Node value, String helper) + throws Exception + { + return value.getNamespaceURI(); + } + } + //---------------------------------------------------------------------------- // Test Cases //---------------------------------------------------------------------------- @@ -158,31 +195,62 @@ } - public void testFunctions() throws Exception + public void testAbstractFunctions() throws Exception { - _child1.setAttribute("bar", "baz"); - _child2.setAttribute("bar", "bargle"); + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") + .bindNamespace("ns", NS1) + .bindFunction(new MyAbstractFunction(NS1, "myfunc")); - XPathFunction myfunc = new XPathFunction() { - public Object evaluate(List args) - throws XPathFunctionException - { - NodeList arg = (NodeList)args.get(0); - Element asElem = (Element)arg.item(0); - return asElem.getAttribute("bar"); - } - }; + assertEquals("", xpath1.evaluateAsString(_child1)); + assertEquals(NS1, xpath1.evaluateAsString(_child2)); - XPathWrapper xpath = new XPathWrapper("ns:myfunc(.)") - .bindNamespace("ns", NS1) - .bindFunction(new QName(NS1, "myfunc"), myfunc); + XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.)") + .bindFunction(new MyAbstractFunction(NS1, "myfunc"), "ns"); - assertEquals("baz", xpath.evaluateAsString(_child1)); - assertEquals("bargle", xpath.evaluateAsString(_child2)); + assertEquals("", xpath2.evaluateAsString(_child1)); + assertEquals(NS1, xpath2.evaluateAsString(_child2)); } + public void testStandardFunctions() throws Exception + { + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.)") + .bindFunction(new QName(NS1, "myfunc", "ns"), + new MyStandardFunction()); + assertEquals("", xpath1.evaluateAsString(_child1)); + assertEquals(NS1, xpath1.evaluateAsString(_child2)); + + XPathWrapper xpath2 = new XPathWrapper("ns:myfunc(.,.)") + .bindFunction(new QName(NS2, "myfunc", "ns"), + new MyStandardFunction(), + 2); + + assertEquals("", xpath2.evaluateAsString(_child1)); + assertEquals(NS1, xpath2.evaluateAsString(_child2)); + } + + + public void testUnresolvableFunction() throws Exception + { + // we call with two arguments, it only gets resolved for one + XPathWrapper xpath1 = new XPathWrapper("ns:myfunc(.,.)") + .bindFunction(new QName(NS2, "myfunc", "ns"), + new MyStandardFunction(), + 1); + try + { + xpath1.evaluateAsString(_child1); + fail("didn't throw even though arity was wrong"); + } + catch (XmlException ee) + { + // success + } + } + + + public void testEqualsAndHashCode() throws Exception { Object obj1a = new XPathWrapper("//foo"); Modified: trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java 2008-12-17 14:03:16 UTC (rev 57) +++ trunk/src/test/java/net/sf/practicalxml/xpath/TestAbstractFunction.java 2008-12-20 11:29:17 UTC (rev 58) @@ -359,4 +359,31 @@ assertEquals(1, fn3.compareTo(fn4)); assertEquals(-1, fn4.compareTo(fn3)); } + + public void testEqualsAndHashcode() throws Exception + { + AbstractFunction<Object> fn1 = new AbstractFunction<Object>("foo", "bar", 1, 4); + AbstractFunction<Object> fn2 = new AbstractFunction<Object>("foo", "bar", 3); + AbstractFunction<Object> fn3 = new AbstractFunction<Object>("foo", "bar", 3); + AbstractFunction<Object> fn4 = new AbstractFunction<Object>("foo", "baz", 3); + AbstractFunction<Object> fn5 = new AbstractFunction<Object>("fzz", "bar", 3); + + assertFalse(fn1.equals(fn2)); + assertFalse(fn2.equals(fn1)); + + assertTrue(fn2.equals(fn3)); + assertTrue(fn3.equals(fn2)); + + assertFalse(fn2.equals(fn4)); + assertFalse(fn4.equals(fn2)); + + assertFalse(fn2.equals(fn5)); + assertFalse(fn5.equals(fn2)); + + assertEquals(fn1.hashCode(), fn2.hashCode()); + assertEquals(fn2.hashCode(), fn3.hashCode()); + + // as of JDK 1.5, this is known to be true; it shouldn't change + assertTrue(fn1.hashCode() != fn4.hashCode()); + } } Added: trunk/src/test/java/net/sf/practicalxml/xpath/TestFunctionResolver.java =================================================================== --- trunk/src/test/java/net/sf/practicalxml/xpath/TestFunctionResolver.java (rev 0) +++ trunk/src/test/java/net/sf/practicalxml/xpath/TestFunctionResolver.java 2008-12-20 11:29:17 UTC (rev 58) @@ -0,0 +1,227 @@ +package net.sf.practicalxml.xpath; + +import java.util.Collections; +import java.util.List; + +import javax.xml.namespace.QName; +import javax.xml.xpath.XPathFunction; +import javax.xml.xpath.XPathFunctionException; + +import net.sf.practicalxml.AbstractTestCase; + + +public class TestFunctionResolver +extends AbstractTestCase +{ +//---------------------------------------------------------------------------- +// Support Code +//---------------------------------------------------------------------------- + + private static class MockStandardFunction + implements XPathFunction + { + public int _calls; + + public Object evaluate(List args) throws XPathFunctionException + { + _calls++; + return null; + } + } + + +//---------------------------------------------------------------------------- +// Test Cases +//---------------------------------------------------------------------------- + + public void testSingleAbstractFunctionMaxRange() throws Exception + { + AbstractFunction<Object> fn = new AbstractFunction<Object>("foo", "bar"); + + FunctionResolver resolver = new FunctionResolver(); + assertSame(resolver, resolver.addFunction(fn)); + + assertSame(fn, resolver.resolveFunction(new QName("foo", "bar"), 0)); + assertSame(fn, resolver.resolveFunction(new QName("foo", "bar"), 1)); + assertSame(fn, resolver.resolveFunction(new QName("foo", "bar"), 10)); + } + + + public void testMultipleAbstractFunctionMaxRange() throws Exception + { + AbstractFunction<Object> fn1 = new AbstractFunction<Object>("foo", "bar"); + AbstractFunction<Object> fn2 = new AbstractFunction<Object>("foo", "baz"); + + FunctionResolver resolver = new FunctionResolver() + .addFunction(fn1) + .addFunction(fn2); + + assertSame(fn1, resolver.resolveFunction(new QName("foo", "bar"), 1)); + assertSame(fn2, resolver.resolveFunction(new QName("foo", "baz"), 1)); + } + + + public void testSingleAbstractFunctionMultiRange() throws Exception + { + AbstractFunction<Object> fn1 = new AbstractFunction<Object>("foo", "bar"); + AbstractFunction<Object> fn2 = new AbstractFunction<Object>("foo", "bar", 1); + AbstractFunction<Object> fn3 = new AbstractFunction<Object>("foo", "bar", 2, 5); + + FunctionResolver resolver = new FunctionResolver() + .addFunction(fn1) + .addFunction(fn2) + .addFunction(fn3); + + assertSame(fn1, resolver.resolveFunction(new QName("foo", "bar"), 0)); + assertSame(fn2, resolver.resolveFunction(new QName("foo", "bar"), 1)); + assertSame(fn3, resolver.resolveFunction(new QName("foo", "bar"), 2)); + assertSame(fn3, resolver.resolveFunction(new QName("foo", "bar"), 5)); + assertSame(fn1, resolver.resolveFunction(new QName("foo", "bar"), 6)); + } + + + public void testOverwriteOnAdd() throws Exception + { + AbstractFunction<Object> fn1 = new AbstractFunction<Object>("foo", "bar", 1); + AbstractFunction<Object> fn2 = new AbstractFunction<Object>("foo", "bar", 1); + AbstractFunction<Object> fn3 = new AbstractFunction<Object>("foo", "bar", 1); + + FunctionResolver resolver = new FunctionResolver() + .addFunction(fn1) + .addFunction(fn2); + + assertSame(fn2, resolver.resolveFunction(new QName("foo", "bar"), 1)); + + resolver.addFunction(fn3); + assertSame(fn3, resolver.resolveFunction(new QName("foo", "bar"), 1)); + } + + + public void testSingleStandardFunctionMaxRange() throws Exception + { + MockStandardFunction xfn = new MockStandardFunction(); + + FunctionResolver resolver = new FunctionResolver() + .addFunction(xfn, new QName("foo", "bar")); + + XPathFunction ret1 = resolver.resolveFunction(new QName("foo", "bar"), 0); + XPathFunction ret2 = resolver.resolveFunction(new QName("foo", "bar"), 0); + XPathFunction ret3 = resolver.resolveFunction(new QName("foo", "bar"), 0); + + assertNotNull(ret1); + assertSame(ret1, ret2); + assertSame(ret1, ret3); + + ret1.evaluate(Collections.EMPTY_LIST); + assertEquals(1, xfn._calls); + } + + + public void testSingleStandardFunctionMultiRange() throws Exception + { + MockStandardFunction xfn1 = new MockStandardFunction(); + MockStandardFunction xfn2 = new MockStandardFunction(); + MockStandardFunction xfn3 = new MockStandardFunction(); + + FunctionResolver resolver = new FunctionResolver() + .addFunction(xfn1, new QName("foo", "bar")) + .addFunction(xfn2, new QName("foo", "bar"), 1) + .addFunction(xfn3, new QName("foo", "bar"), 2, 5); + + resolver.resolveFunction(new QName("foo", "bar"), 0).evaluate(Collections.EMPTY_LIST); + assertEquals(1, xfn1._calls); + assertEquals(0, xfn2._calls); + assertEquals(0, xfn3._calls); + + resolver.resolveFunction(new QName("foo", "bar"), 1).evaluate(Collections.EMPTY_LIST); + assertEquals(1, xfn1._calls); + assertEquals(1, xfn2._calls); + assertEquals(0, xfn3._calls); + + resolver.resolveFunction(new QName("foo", "bar"), 2).evaluate(Collections.EMPTY_LIST); + assertEquals(1, xfn1._calls); + assertEquals(1, xfn2._calls); + assertEquals(1, xfn3._calls); + + resolver.resolveFunction(new QName("foo", "bar"), 5).evaluate(Collections.EMPTY_LIST); + assertEquals(1, xfn1._calls); + assertEquals(1, xfn2._calls); + assertEquals(2, xfn3._calls); + + resolver.resolveFunction(new QName("foo", "bar"), 6).evaluate(Collections.EMPTY_LIST); + assertEquals(2, xfn1._calls); + assertEquals(1, xfn2._calls); + assertEquals(2, xfn3._calls); + } + + + public void testUnresolvedFunctions() throws Exception + { + FunctionResolver resolver = new FunctionResolver() + .addFunction(new AbstractFunction<Object>("foo", "bar", 1)) + .addFunction(new AbstractFunction<Object>("fizz", "buzz", 1)) + .addFunction(new AbstractFunction<Object>("fizz", "buzz", 2)); + + assertNull(resolver.resolveFunction(new QName("f", "b"), 1)); + assertNull(resolver.resolveFunction(new QName("foo", "bar"), 2)); + assertNull(resolver.resolveFunction(new QName("fizz", "buzz"), 3)); + } + + + public void testEqualsAndHashcode() throws Exception + { + AbstractFunction<Object> fn1 = new AbstractFunction<Object>("foo", "bar", 1); + AbstractFunction<Object> fn2 = new AbstractFunction<Object>("foo", "bar", 1, 5); + AbstractFunction<Object> fn3 = new AbstractFunction<Object>("foo", "baz"); + + FunctionResolver resolv1 = new FunctionResolver() + .addFunction(fn1); + FunctionResolver resolv2 = new FunctionResolver() + .addFunction(fn1); + FunctionResolver resolv3 = new FunctionResolver() + .addFunction(fn2); + FunctionResolver resolv4 = new FunctionResolver() + .addFunction(fn1) + .addFunction(fn2); + FunctionResolver resolv5 = new FunctionResolver() + .addFunction(fn1) + .addFunction(fn2); + FunctionResolver resolv6 = new FunctionResolver() + .addFunction(fn1) + .addFunction(fn2) + .addFunction(fn3); + FunctionResolver resolv7 = new FunctionResolver() + .addFunction(fn1) + .addFunction(fn2) + .addFunction(fn3); + + // self and bogus + assertTrue(resolv1.equals(resolv1)); + assertFalse(resolv1.equals(new Object())); + assertFalse(resolv1.equals(null)); + + // single definition + assertTrue(resolv1.equals(resolv2)); + assertTrue(resolv2.equals(resolv1)); + + // multiple definitions, same QName + assertTrue(resolv4.equals(resolv5)); + assertTrue(resolv5.equals(resolv4)); + + // multiple definitions, multiple QNames + assertTrue(resolv6.equals(resolv7)); + assertTrue(resolv7.equals(resolv6)); + + // different arity ranges + assertFalse(resolv1.equals(resolv3)); + assertFalse(resolv3.equals(resolv1)); + + // partial overlap + assertFalse(resolv1.equals(resolv4)); + assertFalse(resolv4.equals(resolv1)); + + // hashcode - the different hashcodes are per test + assertTrue(resolv1.hashCode() == resolv2.hashCode()); + assertTrue(resolv1.hashCode() != resolv7.hashCode()); + } +} This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |