Source Code Cross Referenced for DifferenceEngine.java in  » Testing » XMLUnit » org » custommonkey » xmlunit » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Testing » XMLUnit » org.custommonkey.xmlunit 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


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.util.ArrayList;
040:        import java.util.HashMap;
041:        import java.util.Iterator;
042:        import java.util.List;
043:
044:        import org.w3c.dom.Attr;
045:        import org.w3c.dom.CharacterData;
046:        import org.w3c.dom.CDATASection;
047:        import org.w3c.dom.Comment;
048:        import org.w3c.dom.Document;
049:        import org.w3c.dom.DocumentType;
050:        import org.w3c.dom.Element;
051:        import org.w3c.dom.NamedNodeMap;
052:        import org.w3c.dom.Node;
053:        import org.w3c.dom.NodeList;
054:        import org.w3c.dom.ProcessingInstruction;
055:        import org.w3c.dom.Text;
056:
057:        /**
058:         * Class that has responsibility for comparing Nodes and notifying a
059:         * DifferenceListener of any differences or dissimilarities that are found.
060:         * Knows how to compare namespaces and nested child nodes, but currently
061:         * only compares nodes of type ELEMENT_NODE, CDATA_SECTION_NODE,
062:         * COMMENT_NODE, DOCUMENT_TYPE_NODE, PROCESSING_INSTRUCTION_NODE and TEXT_NODE.
063:         * Nodes of other types (eg ENTITY_NODE) will be skipped.
064:         * <br />Examples and more at <a href="http://xmlunit.sourceforge.net"/>xmlunit.
065:         * sourceforge.net</a>
066:         * @see DifferenceListener#differenceFound(Difference)
067:         */
068:        public class DifferenceEngine implements  DifferenceConstants {
069:            private static final String NULL_NODE = "null";
070:            private static final String NOT_NULL_NODE = "not null";
071:            private static final String ATTRIBUTE_ABSENT = "[attribute absent]";
072:            private final ComparisonController controller;
073:            private final XpathNodeTracker controlTracker;
074:            private final XpathNodeTracker testTracker;
075:
076:            /**
077:             * Simple constructor
078:             * @param controller the instance used to determine whether a Difference
079:             * detected by this class should halt further comparison or not
080:             * @see ComparisonController#haltComparison(Difference)
081:             */
082:            public DifferenceEngine(ComparisonController controller) {
083:                this .controller = controller;
084:                this .controlTracker = new XpathNodeTracker();
085:                this .testTracker = new XpathNodeTracker();
086:            }
087:
088:            /**
089:             * Entry point for Node comparison testing.
090:             * @param control Control XML to compare
091:             * @param test Test XML to compare
092:             * @param listener Notified of any {@link Difference differences} detected
093:             * during node comparison testing
094:             * @param elementQualifier Used to determine which elements qualify for
095:             * comparison e.g. when a node has repeated child elements that may occur
096:             * in any sequence and that sequence is not considered important. 
097:             */
098:            public void compare(Node control, Node test,
099:                    DifferenceListener listener,
100:                    ElementQualifier elementQualifier) {
101:                controlTracker.reset();
102:                testTracker.reset();
103:                try {
104:                    compare(getNullOrNotNull(control), getNullOrNotNull(test),
105:                            control, test, listener, NODE_TYPE);
106:                    if (control != null) {
107:                        compareNode(control, test, listener, elementQualifier);
108:                    }
109:                } catch (DifferenceFoundException e) {
110:                    // thrown by the protected compare() method to terminate the
111:                    // comparison and unwind the call stack back to here
112:                }
113:            }
114:
115:            private String getNullOrNotNull(Node aNode) {
116:                return aNode == null ? NULL_NODE : NOT_NULL_NODE;
117:            }
118:
119:            /**
120:             * First point of call: if nodes are comparable it compares node values then
121:             *  recurses to compare node children.
122:             * @param control
123:             * @param test
124:             * @param listener
125:             * @param elementQualifier
126:             * @throws DifferenceFoundException
127:             */
128:            protected void compareNode(Node control, Node test,
129:                    DifferenceListener listener,
130:                    ElementQualifier elementQualifier)
131:                    throws DifferenceFoundException {
132:                boolean comparable = compareNodeBasics(control, test, listener);
133:                boolean isDocumentNode = false;
134:
135:                if (comparable) {
136:                    switch (control.getNodeType()) {
137:                    case Node.ELEMENT_NODE:
138:                        compareElement((Element) control, (Element) test,
139:                                listener);
140:                        break;
141:                    case Node.CDATA_SECTION_NODE:
142:                    case Node.TEXT_NODE:
143:                        compareText((CharacterData) control,
144:                                (CharacterData) test, listener);
145:                        break;
146:                    case Node.COMMENT_NODE:
147:                        compareComment((Comment) control, (Comment) test,
148:                                listener);
149:                        break;
150:                    case Node.DOCUMENT_TYPE_NODE:
151:                        compareDocumentType((DocumentType) control,
152:                                (DocumentType) test, listener);
153:                        break;
154:                    case Node.PROCESSING_INSTRUCTION_NODE:
155:                        compareProcessingInstruction(
156:                                (ProcessingInstruction) control,
157:                                (ProcessingInstruction) test, listener);
158:                        break;
159:                    case Node.DOCUMENT_NODE:
160:                        isDocumentNode = true;
161:                        compareDocument((Document) control, (Document) test,
162:                                listener, elementQualifier);
163:                        break;
164:                    default:
165:                        listener.skippedComparison(control, test);
166:                    }
167:                }
168:
169:                compareHasChildNodes(control, test, listener);
170:                if (isDocumentNode) {
171:                    Element controlElement = ((Document) control)
172:                            .getDocumentElement();
173:                    Element testElement = ((Document) test)
174:                            .getDocumentElement();
175:                    if (controlElement != null && testElement != null) {
176:                        compareNode(controlElement, testElement, listener,
177:                                elementQualifier);
178:                    }
179:                } else {
180:                    controlTracker.indent();
181:                    testTracker.indent();
182:                    compareNodeChildren(control, test, listener,
183:                            elementQualifier);
184:                    controlTracker.outdent();
185:                    testTracker.outdent();
186:                }
187:            }
188:
189:            /**
190:             * Compare two Documents for doctype and then element differences
191:             * @param control
192:             * @param test
193:             * @param listener
194:             * @param elementQualifier
195:             * @throws DifferenceFoundException
196:             */
197:            protected void compareDocument(Document control, Document test,
198:                    DifferenceListener listener,
199:                    ElementQualifier elementQualifier)
200:                    throws DifferenceFoundException {
201:                DocumentType controlDoctype = control.getDoctype();
202:                DocumentType testDoctype = test.getDoctype();
203:                compare(getNullOrNotNull(controlDoctype),
204:                        getNullOrNotNull(testDoctype), controlDoctype,
205:                        testDoctype, listener, HAS_DOCTYPE_DECLARATION);
206:                if (controlDoctype != null && testDoctype != null) {
207:                    compareNode(controlDoctype, testDoctype, listener,
208:                            elementQualifier);
209:                }
210:            }
211:
212:            /**
213:             * Compares node type and node namespace characteristics: basically
214:             * determines if nodes are comparable further
215:             * @param control
216:             * @param test
217:             * @param listener
218:             * @return true if the nodes are comparable further, false otherwise
219:             * @throws DifferenceFoundException
220:             */
221:            protected boolean compareNodeBasics(Node control, Node test,
222:                    DifferenceListener listener)
223:                    throws DifferenceFoundException {
224:                controlTracker.visited(control);
225:                testTracker.visited(test);
226:
227:                Short controlType = new Short(control.getNodeType());
228:                Short testType = new Short(test.getNodeType());
229:
230:                boolean textAndCDATA = comparingTextAndCDATA(control
231:                        .getNodeType(), test.getNodeType());
232:                if (!textAndCDATA) {
233:                    compare(controlType, testType, control, test, listener,
234:                            NODE_TYPE);
235:                }
236:                compare(control.getNamespaceURI(), test.getNamespaceURI(),
237:                        control, test, listener, NAMESPACE_URI);
238:                compare(control.getPrefix(), test.getPrefix(), control, test,
239:                        listener, NAMESPACE_PREFIX);
240:
241:                return textAndCDATA || controlType.equals(testType);
242:            }
243:
244:            private boolean comparingTextAndCDATA(short controlType,
245:                    short testType) {
246:                return XMLUnit.getIgnoreDiffBetweenTextAndCDATA()
247:                        && (controlType == Node.TEXT_NODE
248:                                && testType == Node.CDATA_SECTION_NODE || testType == Node.TEXT_NODE
249:                                && controlType == Node.CDATA_SECTION_NODE);
250:            }
251:
252:            /**
253:             * Compare the number of children, and if the same, compare the actual
254:             *  children via their NodeLists.
255:             * @param control
256:             * @param test
257:             * @param listener
258:             * @throws DifferenceFoundException
259:             */
260:            protected void compareHasChildNodes(Node control, Node test,
261:                    DifferenceListener listener)
262:                    throws DifferenceFoundException {
263:                Boolean controlHasChildren = hasChildNodes(control);
264:                Boolean testHasChildren = hasChildNodes(test);
265:                compare(controlHasChildren, testHasChildren, control, test,
266:                        listener, HAS_CHILD_NODES);
267:            }
268:
269:            /**
270:             * Tests whether a Node has children, taking ignoreComments
271:             * setting into account.
272:             */
273:            private Boolean hasChildNodes(Node n) {
274:                boolean flag = n.hasChildNodes();
275:                if (flag && XMLUnit.getIgnoreComments()) {
276:                    List nl = nodeList2List(n.getChildNodes());
277:                    flag = !nl.isEmpty();
278:                }
279:                return flag ? Boolean.TRUE : Boolean.FALSE;
280:            }
281:
282:            /**
283:             * Returns the NodeList's Nodes as List, taking ignoreComments
284:             * into account.
285:             */
286:            static List nodeList2List(NodeList nl) {
287:                int len = nl.getLength();
288:                ArrayList l = new ArrayList(len);
289:                for (int i = 0; i < len; i++) {
290:                    Node n = nl.item(i);
291:                    if (!XMLUnit.getIgnoreComments() || !(n instanceof  Comment)) {
292:                        l.add(n);
293:                    }
294:                }
295:                return l;
296:            }
297:
298:            /**
299:             * Compare the number of children, and if the same, compare the actual
300:             *  children via their NodeLists.
301:             * @param control
302:             * @param test
303:             * @param listener
304:             * @param elementQualifier
305:             * @throws DifferenceFoundException
306:             */
307:            protected void compareNodeChildren(Node control, Node test,
308:                    DifferenceListener listener,
309:                    ElementQualifier elementQualifier)
310:                    throws DifferenceFoundException {
311:                if (control.hasChildNodes() && test.hasChildNodes()) {
312:                    List controlChildren = nodeList2List(control
313:                            .getChildNodes());
314:                    List testChildren = nodeList2List(test.getChildNodes());
315:
316:                    Integer controlLength = new Integer(controlChildren.size());
317:                    Integer testLength = new Integer(testChildren.size());
318:                    compare(controlLength, testLength, control, test, listener,
319:                            CHILD_NODELIST_LENGTH);
320:                    compareNodeList(controlChildren, testChildren,
321:                            controlLength.intValue(), listener,
322:                            elementQualifier);
323:                }
324:            }
325:
326:            /**
327:             * Compare the contents of two node list one by one, assuming that order
328:             * of children is NOT important: matching begins at same position in test
329:             * list as control list.
330:             * @param control
331:             * @param test
332:             * @param numNodes convenience parameter because the calling method should
333:             *  know the value already
334:             * @param listener
335:             * @param elementQualifier used to determine which of the child elements in
336:             * the test NodeList should be compared to the current child element in the
337:             * control NodeList.
338:             * @throws DifferenceFoundException
339:             * @deprecated Use the version with List arguments instead
340:             */
341:            protected void compareNodeList(final NodeList control,
342:                    final NodeList test, final int numNodes,
343:                    final DifferenceListener listener,
344:                    final ElementQualifier elementQualifier)
345:                    throws DifferenceFoundException {
346:                compareNodeList(nodeList2List(control), nodeList2List(test),
347:                        numNodes, listener, elementQualifier);
348:            }
349:
350:            /**
351:             * Compare the contents of two node list one by one, assuming that order
352:             * of children is NOT important: matching begins at same position in test
353:             * list as control list.
354:             * @param control
355:             * @param test
356:             * @param numNodes convenience parameter because the calling method should
357:             *  know the value already
358:             * @param listener
359:             * @param elementQualifier used to determine which of the child elements in
360:             * the test NodeList should be compared to the current child element in the
361:             * control NodeList.
362:             * @throws DifferenceFoundException
363:             */
364:            protected void compareNodeList(final List controlChildren,
365:                    final List testChildren, final int numNodes,
366:                    final DifferenceListener listener,
367:                    final ElementQualifier elementQualifier)
368:                    throws DifferenceFoundException {
369:
370:                int j = 0;
371:                final int lastTestNode = testChildren.size() - 1;
372:                testTracker.preloadChildList(testChildren);
373:
374:                HashMap/*<Node, Node>*/matchingNodes = new HashMap();
375:                HashMap/*<Node, Integer>*/matchingNodeIndexes = new HashMap();
376:
377:                List/*<Node>*/unmatchedTestNodes = new ArrayList(testChildren);
378:
379:                // first pass to find the matching nodes in control and test docs
380:                for (int i = 0; i < numNodes; ++i) {
381:                    Node nextControl = (Node) controlChildren.get(i);
382:                    boolean matchOnElement = nextControl instanceof  Element;
383:                    short findNodeType = nextControl.getNodeType();
384:                    int startAt = (i > lastTestNode ? lastTestNode : i);
385:                    j = startAt;
386:
387:                    boolean matchFound = false;
388:
389:                    while (!matchFound) {
390:                        Node t = (Node) testChildren.get(j);
391:                        if (findNodeType == t.getNodeType()
392:                                || comparingTextAndCDATA(findNodeType, t
393:                                        .getNodeType())) {
394:                            matchFound = !matchOnElement
395:                                    || elementQualifier == null
396:                                    || elementQualifier.qualifyForComparison(
397:                                            (Element) nextControl, (Element) t);
398:                        }
399:                        if (!matchFound) {
400:                            ++j;
401:                            if (j > lastTestNode) {
402:                                j = 0;
403:                            }
404:                            if (j == startAt) {
405:                                // been through all children
406:                                break;
407:                            }
408:                        }
409:                    }
410:                    if (matchFound) {
411:                        matchingNodes.put(nextControl, testChildren.get(j));
412:                        matchingNodeIndexes.put(nextControl, new Integer(j));
413:                        unmatchedTestNodes.remove(testChildren.get(j));
414:                    }
415:                }
416:
417:                // next, do the actual comparision on those that matched - or
418:                // match them against the first test nodes that didn't match
419:                // any other control nodes
420:                for (int i = 0; i < numNodes; ++i) {
421:                    Node nextControl = (Node) controlChildren.get(i);
422:                    Node nextTest = (Node) matchingNodes.get(nextControl);
423:                    Integer testIndex = (Integer) matchingNodeIndexes
424:                            .get(nextControl);
425:                    if (nextTest == null && !unmatchedTestNodes.isEmpty()) {
426:                        nextTest = (Node) unmatchedTestNodes.get(0);
427:                        testIndex = new Integer(testChildren.indexOf(nextTest));
428:                        unmatchedTestNodes.remove(0);
429:                    }
430:                    if (nextTest != null) {
431:                        compareNode(nextControl, nextTest, listener,
432:                                elementQualifier);
433:                        compare(new Integer(i), testIndex, nextControl,
434:                                nextTest, listener, CHILD_NODELIST_SEQUENCE);
435:                    } else {
436:                        compare(nextControl.getNodeName(), null, nextControl,
437:                                null, listener, CHILD_NODE_NOT_FOUND);
438:                    }
439:                }
440:
441:                // now handle remaining unmatched test nodes
442:                for (Iterator iter = unmatchedTestNodes.iterator(); iter
443:                        .hasNext();) {
444:                    Node n = (Node) iter.next();
445:                    compare(null, n.getNodeName(), null, n, listener,
446:                            CHILD_NODE_NOT_FOUND);
447:                }
448:            }
449:
450:            /**
451:             * @param aNode
452:             * @return true if the node has a namespace
453:             */
454:            private boolean isNamespaced(Node aNode) {
455:                String namespace = aNode.getNamespaceURI();
456:                return namespace != null && namespace.length() > 0;
457:            }
458:
459:            /**
460:             * Compare 2 elements and their attributes
461:             * @param control
462:             * @param test
463:             * @param listener
464:             * @throws DifferenceFoundException
465:             */
466:            protected void compareElement(Element control, Element test,
467:                    DifferenceListener listener)
468:                    throws DifferenceFoundException {
469:                compare(getUnNamespacedNodeName(control),
470:                        getUnNamespacedNodeName(test), control, test, listener,
471:                        ELEMENT_TAG_NAME);
472:
473:                NamedNodeMap controlAttr = control.getAttributes();
474:                Integer controlNonXmlnsAttrLength = getNonSpecialAttrLength(controlAttr);
475:                NamedNodeMap testAttr = test.getAttributes();
476:                Integer testNonXmlnsAttrLength = getNonSpecialAttrLength(testAttr);
477:                compare(controlNonXmlnsAttrLength, testNonXmlnsAttrLength,
478:                        control, test, listener, ELEMENT_NUM_ATTRIBUTES);
479:
480:                compareElementAttributes(control, test, controlAttr, testAttr,
481:                        listener);
482:            }
483:
484:            /**
485:             * The number of attributes not related to namespace declarations
486:             * and/or Schema location.
487:             */
488:            private Integer getNonSpecialAttrLength(NamedNodeMap attributes) {
489:                int length = 0, maxLength = attributes.getLength();
490:                for (int i = 0; i < maxLength; ++i) {
491:                    Attr a = (Attr) attributes.item(i);
492:                    if (!isXMLNSAttribute(a)
493:                            && !isRecognizedXMLSchemaInstanceAttribute(a)) {
494:                        ++length;
495:                    }
496:                }
497:                return new Integer(length);
498:            }
499:
500:            void compareElementAttributes(Element control, Element test,
501:                    NamedNodeMap controlAttr, NamedNodeMap testAttr,
502:                    DifferenceListener listener)
503:                    throws DifferenceFoundException {
504:                ArrayList unmatchedTestAttrs = new ArrayList();
505:                for (int i = 0; i < testAttr.getLength(); ++i) {
506:                    Attr nextAttr = (Attr) testAttr.item(i);
507:                    if (!isXMLNSAttribute(nextAttr)) {
508:                        unmatchedTestAttrs.add(nextAttr);
509:                    }
510:                }
511:
512:                for (int i = 0; i < controlAttr.getLength(); ++i) {
513:                    Attr nextAttr = (Attr) controlAttr.item(i);
514:                    if (isXMLNSAttribute(nextAttr)) {
515:                        // xml namespacing is handled in compareNodeBasics
516:                    } else {
517:                        boolean isNamespacedAttr = isNamespaced(nextAttr);
518:                        String attrName = getUnNamespacedNodeName(nextAttr,
519:                                isNamespacedAttr);
520:                        Attr compareTo = null;
521:
522:                        if (isNamespacedAttr) {
523:                            compareTo = (Attr) testAttr.getNamedItemNS(nextAttr
524:                                    .getNamespaceURI(), attrName);
525:                        } else {
526:                            compareTo = (Attr) testAttr.getNamedItem(attrName);
527:                        }
528:
529:                        if (compareTo != null) {
530:                            unmatchedTestAttrs.remove(compareTo);
531:                        }
532:
533:                        if (isRecognizedXMLSchemaInstanceAttribute(nextAttr)) {
534:                            compareRecognizedXMLSchemaInstanceAttribute(
535:                                    nextAttr, compareTo, listener);
536:
537:                        } else if (compareTo != null) {
538:                            compareAttribute(nextAttr, compareTo, listener);
539:
540:                            if (!XMLUnit.getIgnoreAttributeOrder()) {
541:                                Attr attributeItem = (Attr) testAttr.item(i);
542:                                String testAttrName = ATTRIBUTE_ABSENT;
543:                                if (attributeItem != null) {
544:                                    testAttrName = getUnNamespacedNodeName(attributeItem);
545:                                }
546:                                compare(attrName, testAttrName, nextAttr,
547:                                        compareTo, listener, ATTR_SEQUENCE);
548:                            }
549:                        } else {
550:                            compare(attrName, null, control, test, listener,
551:                                    ATTR_NAME_NOT_FOUND);
552:                        }
553:                    }
554:                }
555:
556:                for (Iterator iter = unmatchedTestAttrs.iterator(); iter
557:                        .hasNext();) {
558:                    Attr nextAttr = (Attr) iter.next();
559:                    if (isRecognizedXMLSchemaInstanceAttribute(nextAttr)) {
560:                        compareRecognizedXMLSchemaInstanceAttribute(null,
561:                                nextAttr, listener);
562:                    } else {
563:                        compare(null, getUnNamespacedNodeName(nextAttr,
564:                                isNamespaced(nextAttr)), control, test,
565:                                listener, ATTR_NAME_NOT_FOUND);
566:                    }
567:                }
568:
569:                controlTracker.clearTrackedAttribute();
570:                testTracker.clearTrackedAttribute();
571:            }
572:
573:            private String getUnNamespacedNodeName(Node aNode) {
574:                return getUnNamespacedNodeName(aNode, isNamespaced(aNode));
575:            }
576:
577:            private String getUnNamespacedNodeName(Node aNode,
578:                    boolean isNamespacedNode) {
579:                if (isNamespacedNode) {
580:                    return aNode.getLocalName();
581:                }
582:                return aNode.getNodeName();
583:            }
584:
585:            /**
586:             * @param attribute
587:             * @return true if the attribute represents a namespace declaration
588:             */
589:            private boolean isXMLNSAttribute(Attr attribute) {
590:                return XMLConstants.XMLNS_PREFIX.equals(attribute.getPrefix())
591:                        || XMLConstants.XMLNS_PREFIX
592:                                .equals(attribute.getName());
593:            }
594:
595:            /**
596:             * @param attr
597:             * @return true if the attribute is an XML Schema Instance
598:             * namespace attribute XMLUnit treats in a special way.
599:             */
600:            private boolean isRecognizedXMLSchemaInstanceAttribute(Attr attr) {
601:                return XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI.equals(attr
602:                        .getNamespaceURI())
603:                        && (XMLConstants.W3C_XML_SCHEMA_INSTANCE_SCHEMA_LOCATION_ATTR
604:                                .equals(attr.getLocalName()) || XMLConstants.W3C_XML_SCHEMA_INSTANCE_NO_NAMESPACE_SCHEMA_LOCATION_ATTR
605:                                .equals(attr.getLocalName()));
606:            }
607:
608:            /**
609:             * Compare two attributes
610:             * @param control
611:             * @param test
612:             * @param listener
613:             * @throws DifferenceFoundException
614:             */
615:            protected void compareRecognizedXMLSchemaInstanceAttribute(
616:                    Attr control, Attr test, DifferenceListener listener)
617:                    throws DifferenceFoundException {
618:                Attr nonNullNode = control != null ? control : test;
619:                Difference d = XMLConstants.W3C_XML_SCHEMA_INSTANCE_SCHEMA_LOCATION_ATTR
620:                        .equals(nonNullNode.getLocalName()) ? SCHEMA_LOCATION
621:                        : NO_NAMESPACE_SCHEMA_LOCATION;
622:
623:                if (control != null) {
624:                    controlTracker.visited(control);
625:                }
626:                if (test != null) {
627:                    testTracker.visited(test);
628:                }
629:
630:                compare(
631:                        control != null ? control.getValue() : ATTRIBUTE_ABSENT,
632:                        test != null ? test.getValue() : ATTRIBUTE_ABSENT,
633:                        control, test, listener, d);
634:            }
635:
636:            /**
637:             * Compare two attributes
638:             * @param control
639:             * @param test
640:             * @param listener
641:             * @throws DifferenceFoundException
642:             */
643:            protected void compareAttribute(Attr control, Attr test,
644:                    DifferenceListener listener)
645:                    throws DifferenceFoundException {
646:                controlTracker.visited(control);
647:                testTracker.visited(test);
648:
649:                compare(control.getPrefix(), test.getPrefix(), control, test,
650:                        listener, NAMESPACE_PREFIX);
651:
652:                compare(control.getValue(), test.getValue(), control, test,
653:                        listener, ATTR_VALUE);
654:
655:                compare(control.getSpecified() ? Boolean.TRUE : Boolean.FALSE,
656:                        test.getSpecified() ? Boolean.TRUE : Boolean.FALSE,
657:                        control, test, listener,
658:                        ATTR_VALUE_EXPLICITLY_SPECIFIED);
659:            }
660:
661:            /**
662:             * Compare two CDATA sections - unused, kept for backwards compatibility
663:             * @param control
664:             * @param test
665:             * @param listener
666:             * @throws DifferenceFoundException
667:             */
668:            protected void compareCDataSection(CDATASection control,
669:                    CDATASection test, DifferenceListener listener)
670:                    throws DifferenceFoundException {
671:                compareText(control, test, listener);
672:            }
673:
674:            /**
675:             * Compare two comments
676:             * @param control
677:             * @param test
678:             * @param listener
679:             * @throws DifferenceFoundException
680:             */
681:            protected void compareComment(Comment control, Comment test,
682:                    DifferenceListener listener)
683:                    throws DifferenceFoundException {
684:                if (!XMLUnit.getIgnoreComments()) {
685:                    compareCharacterData(control, test, listener, COMMENT_VALUE);
686:                }
687:            }
688:
689:            /**
690:             * Compare two DocumentType nodes
691:             * @param control
692:             * @param test
693:             * @param listener
694:             * @throws DifferenceFoundException
695:             */
696:            protected void compareDocumentType(DocumentType control,
697:                    DocumentType test, DifferenceListener listener)
698:                    throws DifferenceFoundException {
699:                compare(control.getName(), test.getName(), control, test,
700:                        listener, DOCTYPE_NAME);
701:                compare(control.getPublicId(), test.getPublicId(), control,
702:                        test, listener, DOCTYPE_PUBLIC_ID);
703:
704:                compare(control.getSystemId(), test.getSystemId(), control,
705:                        test, listener, DOCTYPE_SYSTEM_ID);
706:            }
707:
708:            /**
709:             * Compare two processing instructions
710:             * @param control
711:             * @param test
712:             * @param listener
713:             * @throws DifferenceFoundException
714:             */
715:            protected void compareProcessingInstruction(
716:                    ProcessingInstruction control, ProcessingInstruction test,
717:                    DifferenceListener listener)
718:                    throws DifferenceFoundException {
719:                compare(control.getTarget(), test.getTarget(), control, test,
720:                        listener, PROCESSING_INSTRUCTION_TARGET);
721:                compare(control.getData(), test.getData(), control, test,
722:                        listener, PROCESSING_INSTRUCTION_DATA);
723:            }
724:
725:            /**
726:             * Compare text - unused, kept for backwards compatibility
727:             * @param control
728:             * @param test
729:             * @param listener
730:             * @throws DifferenceFoundException
731:             */
732:            protected void compareText(Text control, Text test,
733:                    DifferenceListener listener)
734:                    throws DifferenceFoundException {
735:                compareText((CharacterData) control, (CharacterData) test,
736:                        listener);
737:            }
738:
739:            /**
740:             * Compare text
741:             * @param control
742:             * @param test
743:             * @param listener
744:             * @throws DifferenceFoundException
745:             */
746:            protected void compareText(CharacterData control,
747:                    CharacterData test, DifferenceListener listener)
748:                    throws DifferenceFoundException {
749:                compareCharacterData(control, test, listener,
750:                        control instanceof  CDATASection ? CDATA_VALUE
751:                                : TEXT_VALUE);
752:            }
753:
754:            /**
755:             * Character comparison method used by comments, text and CDATA sections
756:             * @param control
757:             * @param test
758:             * @param listener
759:             * @param differenceType
760:             * @throws DifferenceFoundException
761:             */
762:            private void compareCharacterData(CharacterData control,
763:                    CharacterData test, DifferenceListener listener,
764:                    Difference difference) throws DifferenceFoundException {
765:                compare(control.getData(), test.getData(), control, test,
766:                        listener, difference);
767:            }
768:
769:            /**
770:             * If the expected and actual values are unequal then inform the listener of
771:             *  a difference and throw a DifferenceFoundException.
772:             * @param expected
773:             * @param actual
774:             * @param control
775:             * @param test
776:             * @param listener
777:             * @param differenceType
778:             * @throws DifferenceFoundException
779:             */
780:            protected void compare(Object expected, Object actual,
781:                    Node control, Node test, DifferenceListener listener,
782:                    Difference difference) throws DifferenceFoundException {
783:                if (unequal(expected, actual)) {
784:                    NodeDetail controlDetail = new NodeDetail(String
785:                            .valueOf(expected), control, controlTracker
786:                            .toXpathString());
787:                    NodeDetail testDetail = new NodeDetail(String
788:                            .valueOf(actual), test, testTracker.toXpathString());
789:                    Difference differenceInstance = new Difference(difference,
790:                            controlDetail, testDetail);
791:                    listener.differenceFound(differenceInstance);
792:                    if (controller.haltComparison(differenceInstance)) {
793:                        throw flowControlException;
794:                    }
795:                }
796:            }
797:
798:            /**
799:             * Test two possibly null values for inequality
800:             * @param expected
801:             * @param actual
802:             * @return TRUE if the values are neither both null, nor equals() equal
803:             */
804:            private boolean unequal(Object expected, Object actual) {
805:                return (expected == null ? actual != null : unequalNotNull(
806:                        expected, actual));
807:            }
808:
809:            /**
810:             * Test two non-null values for inequality
811:             * @param expected
812:             * @param actual
813:             * @return TRUE if the values are not equals() equal (taking whitespace
814:             *  into account if necessary)
815:             */
816:            private boolean unequalNotNull(Object expected, Object actual) {
817:                if ((XMLUnit.getIgnoreWhitespace() || XMLUnit
818:                        .getNormalizeWhitespace())
819:                        && expected instanceof  String
820:                        && actual instanceof  String) {
821:                    String expectedString = ((String) expected).trim();
822:                    String actualString = ((String) actual).trim();
823:                    if (XMLUnit.getNormalizeWhitespace()) {
824:                        expectedString = normalizeWhitespace(expectedString);
825:                        actualString = normalizeWhitespace(actualString);
826:                    }
827:                    return !expectedString.equals(actualString);
828:                }
829:                return !(expected.equals(actual));
830:            }
831:
832:            /**
833:             * Replace all whitespace characters with SPACE and collapse
834:             * consecutive whitespace chars to a single SPACE.
835:             */
836:            final static String normalizeWhitespace(String orig) {
837:                StringBuffer sb = new StringBuffer();
838:                boolean lastCharWasWhitespace = false;
839:                boolean changed = false;
840:                char[] characters = orig.toCharArray();
841:                for (int i = 0; i < characters.length; i++) {
842:                    if (Character.isWhitespace(characters[i])) {
843:                        if (lastCharWasWhitespace) {
844:                            // suppress character
845:                            changed = true;
846:                        } else {
847:                            sb.append(' ');
848:                            changed |= characters[i] != ' ';
849:                            lastCharWasWhitespace = true;
850:                        }
851:                    } else {
852:                        sb.append(characters[i]);
853:                        lastCharWasWhitespace = false;
854:                    }
855:                }
856:                return changed ? sb.toString() : orig;
857:            }
858:
859:            /**
860:             * Marker exception thrown by the protected compare() method and passed
861:             * upwards through the call stack to the public compare() method.
862:             */
863:            protected static final class DifferenceFoundException extends
864:                    Exception {
865:                private DifferenceFoundException() {
866:                    super ("This exception is used to control flow");
867:                }
868:            }
869:
870:            /**
871:             * Exception instance used internally to control flow
872:             * when a difference is found
873:             */
874:            private static final DifferenceFoundException flowControlException = new DifferenceFoundException();
875:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.