001: /*
002: ******************************************************************
003: Copyright (c) 2001-2007, Jeff Martin, Tim Bacon
004: All rights reserved.
005:
006: Redistribution and use in source and binary forms, with or without
007: modification, are permitted provided that the following conditions
008: are met:
009:
010: * Redistributions of source code must retain the above copyright
011: notice, this list of conditions and the following disclaimer.
012: * Redistributions in binary form must reproduce the above
013: copyright notice, this list of conditions and the following
014: disclaimer in the documentation and/or other materials provided
015: with the distribution.
016: * Neither the name of the xmlunit.sourceforge.net nor the names
017: of its contributors may be used to endorse or promote products
018: derived from this software without specific prior written
019: permission.
020:
021: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
022: "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
023: LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
024: FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
025: COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
026: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
027: BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
028: LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
029: CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
030: LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
031: ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
032: POSSIBILITY OF SUCH DAMAGE.
033:
034: ******************************************************************
035: */
036:
037: package org.custommonkey.xmlunit;
038:
039: import java.io.IOException;
040: import java.io.Reader;
041: import java.io.StringReader;
042:
043: import org.w3c.dom.Document;
044: import org.w3c.dom.Node;
045: import org.w3c.dom.traversal.DocumentTraversal;
046: import org.w3c.dom.traversal.NodeFilter;
047: import org.w3c.dom.traversal.NodeIterator;
048: import org.xml.sax.InputSource;
049: import org.xml.sax.SAXException;
050:
051: /**
052: * Encapsulation of the Node-by-Node testing of a DOM Document
053: * Uses a nodetype-specific <code>NodeFilter</code> to pass the DOM Nodes
054: * to a NodeTester instance that performs the acual Node validation.
055: * <br />Examples and more at <a href="http://xmlunit.sourceforge.net"/>xmlunit.
056: * sourceforge.net</a>
057: * @see NodeTester
058: */
059: public class NodeTest {
060: private final DocumentTraversal documentTraversal;
061: private final Node rootNode;
062:
063: /**
064: * Construct a NodeTest for the DOM built using the String and JAXP
065: */
066: public NodeTest(String xmlString) throws SAXException, IOException {
067: this (new StringReader(xmlString));
068: }
069:
070: /**
071: * Construct a NodeTest for the DOM built using the Reader and JAXP
072: */
073: public NodeTest(Reader reader) throws SAXException, IOException {
074: this (XMLUnit.buildDocument(XMLUnit.newControlParser(), reader));
075: }
076:
077: /**
078: * Construct a NodeTest for the DOM built using the InputSource.
079: */
080: public NodeTest(InputSource src) throws SAXException, IOException {
081: this (XMLUnit.buildDocument(XMLUnit.newControlParser(), src));
082: }
083:
084: /**
085: * Construct a NodeTest for the specified Document
086: * @exception IllegalArgumentException if the Document does not support the DOM
087: * DocumentTraversal interface (most DOM implementations should provide this
088: * support)
089: */
090: public NodeTest(Document document) {
091: this (getDocumentTraversal(document), document
092: .getDocumentElement());
093: }
094:
095: /**
096: * Try to cast a Document into a DocumentTraversal
097: * @param document
098: * @return DocumentTraversal interface if the DOM implementation supports it
099: */
100: private static DocumentTraversal getDocumentTraversal(
101: Document document) {
102: try {
103: return (DocumentTraversal) document;
104: } catch (ClassCastException e) {
105: throw new IllegalArgumentException(
106: "DOM Traversal not supported by "
107: + document.getImplementation().getClass()
108: .getName()
109: + ". To use this class you will need to switch to a DOM implementation that supports Traversal.");
110: }
111: }
112:
113: /**
114: * Construct a NodeTest using the specified DocumentTraversal, starting at
115: * the specified root node
116: */
117: public NodeTest(DocumentTraversal documentTraversal, Node rootNode) {
118: this .documentTraversal = documentTraversal;
119: this .rootNode = rootNode;
120: }
121:
122: /**
123: * Does this NodeTest pass using the specified NodeTester instance?
124: * @param tester
125: * @param singleNodeType note <code>Node.ATTRIBUTE_NODE</code> is not
126: * exposed by the DocumentTraversal node iterator unless the root node
127: * is itself an attribute - so a NodeTester that needs to test attributes
128: * should obtain those attributes from <code>Node.ELEMENT_NODE</code>
129: * nodes
130: * @exception NodeTestException if test fails
131: */
132: public void performTest(NodeTester tester, short singleNodeType)
133: throws NodeTestException {
134: performTest(tester, new short[] { singleNodeType });
135: }
136:
137: /**
138: * Does this NodeTest pass using the specified NodeTester instance?
139: * @param tester
140: * @param nodeTypes note <code>Node.ATTRIBUTE_NODE</code> is not
141: * exposed by the DocumentTraversal node iterator unless the root node
142: * is itself an attribute - so a NodeTester that needs to test attributes
143: * should obtain those attributes from <code>Node.ELEMENT_NODE</code>
144: * nodes instead
145: * @exception NodeTestException if test fails
146: */
147: public void performTest(NodeTester tester, short[] nodeTypes)
148: throws NodeTestException {
149: NodeIterator iter = documentTraversal.createNodeIterator(
150: rootNode, NodeFilter.SHOW_ALL, new NodeTypeNodeFilter(
151: nodeTypes), true);
152:
153: for (Node nextNode = iter.nextNode(); nextNode != null; nextNode = iter
154: .nextNode()) {
155: tester.testNode(nextNode, this );
156: }
157: tester.noMoreNodes(this );
158: }
159:
160: /**
161: * Node type specific Node Filter: accepts Nodes of those types specified
162: * in constructor, rejects all others
163: */
164: private static class NodeTypeNodeFilter implements NodeFilter {
165: private final short[] nodeTypes;
166:
167: /**
168: * Construct filter for specific node types
169: * @param nodeTypes note <code>Node.ATTRIBUTE_NODE</code> is not
170: * exposed by the DocumentTraversal node iterator unless the root node
171: * is itself an attribute - so a NodeTester that needs to test attributes
172: * should obtain those attributes from <code>Node.ELEMENT_NODE</code>
173: * nodes
174: */
175: public NodeTypeNodeFilter(short[] nodeTypes) {
176: this .nodeTypes = nodeTypes;
177: }
178:
179: /**
180: * NodeFilter method.
181: * @param aNode
182: * @return
183: */
184: public short acceptNode(Node aNode) {
185: if (acceptNodeType(aNode.getNodeType())) {
186: return NodeFilter.FILTER_ACCEPT;
187: }
188: return NodeFilter.FILTER_REJECT;
189: }
190:
191: /**
192: * Does this instance accept nodes with the node type value
193: * @param shortVal
194: * @return
195: */
196: private boolean acceptNodeType(short shortVal) {
197: for (int i = 0; i < nodeTypes.length; ++i) {
198: if (nodeTypes[i] == shortVal) {
199: return true;
200: }
201: }
202: return false;
203: }
204: }
205: }
|