From: <bo...@us...> - 2013-02-03 06:37:45
|
Revision: 503 http://xmlunit.svn.sourceforge.net/xmlunit/?rev=503&view=rev Author: bodewig Date: 2013-02-03 06:37:37 +0000 (Sun, 03 Feb 2013) Log Message: ----------- port xsi:type special handling from 1.x branch Modified Paths: -------------- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Nodes.java trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_Diff.java Property Changed: ---------------- trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_Diff.java Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java 2013-02-02 14:35:56 UTC (rev 502) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/DOMDifferenceEngine.java 2013-02-03 06:37:37 UTC (rev 503) @@ -328,7 +328,6 @@ testContext .addAttributes(Linqy.map(testAttributes.remainingAttributes, QNAME_MAPPER)); - Set<Attr> foundTestAttributes = new HashSet<Attr>(); lastResult = compare(new Comparison(ComparisonType.ELEMENT_NUM_ATTRIBUTES, @@ -340,6 +339,7 @@ return lastResult; } + Set<Attr> foundTestAttributes = new HashSet<Attr>(); for (Attr controlAttr : controlAttributes.remainingAttributes) { final Attr testAttr = findMatchingAttr(testAttributes.remainingAttributes, @@ -393,6 +393,12 @@ } } + lastResult = compareXsiType(controlAttributes.type, controlContext, + testAttributes.type, testContext); + if (lastResult == ComparisonResult.CRITICAL) { + return lastResult; + } + lastResult = compare(new Comparison(ComparisonType.SCHEMA_LOCATION, control, getXPath(controlContext), @@ -537,6 +543,68 @@ } /** + * Compares xsi:type attribute values + */ + private ComparisonResult compareXsiType(Attr controlAttr, + XPathContext controlContext, + Attr testAttr, + XPathContext testContext) { + boolean mustChangeControlContext = controlAttr != null; + boolean mustChangeTestContext = testAttr != null; + if (!mustChangeControlContext && !mustChangeTestContext) { + return ComparisonResult.EQUAL; + } + + try { + if (mustChangeControlContext) { + QName q = Nodes.getQName(controlAttr); + controlContext.addAttribute(q); + controlContext.navigateToAttribute(q); + } + if (mustChangeTestContext) { + QName q = Nodes.getQName(testAttr); + testContext.addAttribute(q); + testContext.navigateToAttribute(q); + } + ComparisonResult lastResult = + compare(new Comparison(ComparisonType.ATTR_NAME_LOOKUP, + controlAttr, getXPath(controlContext), + mustChangeControlContext, + testAttr, getXPath(testContext), + mustChangeTestContext)); + if (lastResult == ComparisonResult.CRITICAL) { + return lastResult; + } + if (mustChangeControlContext && mustChangeTestContext) { + lastResult = + compareAttributeExplicitness(controlAttr, controlContext, + testAttr, testContext); + if (lastResult == ComparisonResult.CRITICAL) { + return lastResult; + } + QName controlQName = valueAsQName(controlAttr); + QName testQName = valueAsQName(testAttr); + lastResult = + compare(new Comparison(ComparisonType.ATTR_VALUE, + controlAttr, + getXPath(controlContext), + controlQName.toString(), + testAttr, + getXPath(testContext), + testQName.toString())); + } + return lastResult; + } finally { + if (mustChangeControlContext) { + controlContext.navigateToParent(); + } + if (mustChangeTestContext) { + testContext.navigateToParent(); + } + } + } + + /** * Compares properties of an attribute. */ private ComparisonResult compareAttributes(Attr control, @@ -544,11 +612,8 @@ Attr test, XPathContext testContext) { ComparisonResult lastResult = - compare(new Comparison(ComparisonType.ATTR_VALUE_EXPLICITLY_SPECIFIED, - control, getXPath(controlContext), - control.getSpecified(), - test, getXPath(testContext), - test.getSpecified())); + compareAttributeExplicitness(control, controlContext, test, + testContext); if (lastResult == ComparisonResult.CRITICAL) { return lastResult; } @@ -561,6 +626,20 @@ } /** + * Compares whether two attributes are specified explicitly. + */ + private ComparisonResult + compareAttributeExplicitness(Attr control, XPathContext controlContext, + Attr test, XPathContext testContext) { + return + compare(new Comparison(ComparisonType.ATTR_VALUE_EXPLICITLY_SPECIFIED, + control, getXPath(controlContext), + control.getSpecified(), + test, getXPath(testContext), + test.getSpecified())); + } + + /** * Separates XML namespace related attributes from "normal" attributes.xb */ private static Attributes splitAttributes(final NamedNodeMap map) { @@ -570,28 +649,45 @@ Attr nNsLoc = (Attr) map.getNamedItemNS(XMLConstants .W3C_XML_SCHEMA_INSTANCE_NS_URI, "noNamespaceSchemaLocation"); + Attr type = (Attr) map.getNamedItemNS(XMLConstants + .W3C_XML_SCHEMA_INSTANCE_NS_URI, + "type"); List<Attr> rest = new LinkedList<Attr>(); final int len = map.getLength(); for (int i = 0; i < len; i++) { Attr a = (Attr) map.item(i); if (!XMLConstants.XMLNS_ATTRIBUTE_NS_URI.equals(a.getNamespaceURI()) - && - !XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI - .equals(a.getNamespaceURI())) { + && a != sLoc && a != nNsLoc && a != type) { rest.add(a); } } - return new Attributes(sLoc, nNsLoc, rest); + return new Attributes(sLoc, nNsLoc, type, rest); } + private static QName valueAsQName(Attr attribute) { + String[] pieces = attribute.getValue().split(":"); + if (pieces.length < 2) { + pieces = new String[] { "", pieces[0] }; + } else if (pieces.length > 2) { + pieces = new String[] { + pieces[0], + attribute.getValue().substring(pieces[0].length() + 1) + }; + } + return new QName(Nodes.findNamespaceURIForPrefix(attribute, pieces[0]), + pieces[1], pieces[0]); + } + private static class Attributes { private final Attr schemaLocation; private final Attr noNamespaceSchemaLocation; + private final Attr type; private final List<Attr> remainingAttributes; private Attributes(Attr schemaLocation, Attr noNamespaceSchemaLocation, - List<Attr> remainingAttributes) { + Attr type, List<Attr> remainingAttributes) { this.schemaLocation = schemaLocation; this.noNamespaceSchemaLocation = noNamespaceSchemaLocation; + this.type = type; this.remainingAttributes = remainingAttributes; } } Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java 2013-02-02 14:35:56 UTC (rev 502) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/diff/XPathContext.java 2013-02-03 06:37:37 UTC (rev 503) @@ -70,6 +70,12 @@ } } + public void addAttribute(QName attribute) { + Level current = path.getLast(); + current.attributes.put(attribute, + new Level(ATTR + getName(attribute))); + } + public void setChildren(Iterable<? extends NodeInfo> children) { Level current = path.getLast(); current.children.clear(); Modified: trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Nodes.java =================================================================== --- trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Nodes.java 2013-02-02 14:35:56 UTC (rev 502) +++ trunk/xmlunit/src/main/java-core/net/sf/xmlunit/util/Nodes.java 2013-02-03 06:37:37 UTC (rev 503) @@ -113,6 +113,35 @@ } /** + * Looks up the namespace URI associated with a given prefix on a given node. + * @param onNode the reference node + * @param prefix the prefix to look for + * @return the URI or null of it cannot be found + */ + public static String findNamespaceURIForPrefix(Node onNode, String prefix) { + if (onNode != null && onNode instanceof Attr) { + onNode = ((Attr) onNode).getOwnerElement(); + } + while (onNode != null && onNode.getNodeType() != Node.ELEMENT_NODE) { + onNode = onNode.getParentNode(); + } + if (onNode == null) { + return null; + } + + if (prefix == null || "".equals(prefix)) { + prefix = XMLConstants.XMLNS_ATTRIBUTE; + } + Map<QName, String> attrs = getAttributes(onNode); + String uri = attrs.get(new QName(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, + prefix)); + if (uri != null) { + return uri; + } + return findNamespaceURIForPrefix(onNode.getParentNode(), prefix); + } + + /** * Trims textual content of this node, removes empty text and * CDATA children, recurses into its child nodes. * @param normalize whether to normalize whitespace as well Modified: trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_Diff.java =================================================================== --- trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_Diff.java 2013-02-02 14:35:56 UTC (rev 502) +++ trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_Diff.java 2013-02-03 06:37:37 UTC (rev 503) @@ -997,7 +997,45 @@ assertTrue(diff.toString(), diff.similar()); } - public void XtestXsiTypeSpecialCaseDoesntIgnorePrefix() throws Exception { + public void testXsiTypeSpecialCaseShortLocalName() throws Exception { + String test = "<ns1:Square xsi:type=\"ns1:a\" " + + "xmlns:ns1=\"http://example.com/\" " + + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>"; + + String control = "<ns2:Square xsi:type=\"ns2:a\" " + + "xmlns:ns2=\"http://example.com/\" " + + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>"; + Diff diff = new Diff(control, test); + assertTrue(diff.toString(), diff.similar()); + } + + public void testXsiTypeSpecialCaseWorksWithDefaultNs() throws Exception { + String test = "<Square xsi:type=\"Shape\" " + + "xmlns=\"http://example.com/\" " + + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>"; + + String control = "<ns2:Square xsi:type=\"ns2:Shape\" " + + "xmlns:ns2=\"http://example.com/\" " + + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>"; + Diff diff = new Diff(control, test); + assertTrue(diff.toString(), diff.similar()); + } + + public void testXsiTypeSpecialCaseInheritsParentNs() throws Exception { + String test = "<ns1:Shapes xmlns:ns1=\"http://example.com/\">" + + "<ns1:Square xsi:type=\"ns1:Shape\" " + + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>" + + "</ns1:Shapes>"; + + String control = "<ns2:Shapes xmlns:ns2=\"http://example.com/\">" + + "<ns2:Square xsi:type=\"ns2:Shape\" " + + "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"/>" + + "</ns2:Shapes>"; + Diff diff = new Diff(control, test); + assertTrue(diff.toString(), diff.similar()); + } + + public void testXsiTypeSpecialCaseDoesntIgnorePrefix() throws Exception { String test = "<ns1:Square xsi:type=\"ns1:Shape\" " + "xmlns:ns1=\"http://example.com/\" " + "xmlns:ns2=\"http://example.com/another-uri/\" " Property changes on: trunk/xmlunit/src/tests/java-legacy/org/custommonkey/xmlunit/test_Diff.java ___________________________________________________________________ Modified: svn:mergeinfo - /branches/xmlunit-1.x/tests/java/org/custommonkey/xmlunit/test_Diff.java:337,346,353,494,499 /branches/xmlunit-1.x/tests/org/custommonkey/xmlunit/test_Diff.java:346 + /branches/xmlunit-1.x/tests/java/org/custommonkey/xmlunit/test_Diff.java:337,346,353,494,499,501 /branches/xmlunit-1.x/tests/org/custommonkey/xmlunit/test_Diff.java:346 This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |