diff options
author | Jesse Wilson <jessewilson@google.com> | 2010-02-20 11:11:40 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-02-20 11:11:40 -0800 |
commit | 5266a158422ee13eefc577f82b50fa0e4f36e381 (patch) | |
tree | 7c5a73feba8848b33b14140b6d6da48d3fc99e10 /xml | |
parent | 67cc34b75c18ae7d02c90282cb8616b82e1d8fcb (diff) | |
parent | 35d7c089bd45f1030407b9b69b46e66f02c03043 (diff) | |
download | libcore-5266a158422ee13eefc577f82b50fa0e4f36e381.zip libcore-5266a158422ee13eefc577f82b50fa0e4f36e381.tar.gz libcore-5266a158422ee13eefc577f82b50fa0e4f36e381.tar.bz2 |
Merge "More XML DOM v3 APIs."
Diffstat (limited to 'xml')
4 files changed, 324 insertions, 14 deletions
diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java index 861f0a3..834cc47 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java @@ -50,17 +50,24 @@ public class DOMImplementationImpl implements DOMImplementation { } public boolean hasFeature(String feature, String version) { - // We claim to support DOM Core Level 1 & 2, nothing else. + boolean anyVersion = version == null || version.length() == 0; + if (feature.startsWith("+")) { + feature = feature.substring(1); + } - // TODO + // TODO: fully implement these APIs: + // "LS" (org.w3c.dom.ls) versions "3.0" + // "ElementTraversal" (org.w3c.dom.traversal) versions "1.0" - if ("Core".equalsIgnoreCase(feature) || "XML".equalsIgnoreCase(feature)) { - if (version == null || "".equals(version) || "1.0".equals(version) || "2.0".equals(version)) { - return true; - } + if (feature.equalsIgnoreCase("Core")) { + return anyVersion || version.equals("1.0") || version.equals("2.0") || version.equals("3.0"); + } else if (feature.equalsIgnoreCase("XML")) { + return anyVersion || version.equals("1.0") || version.equals("2.0") || version.equals("3.0"); + } else if (feature.equalsIgnoreCase("XMLVersion")) { + return anyVersion || version.equals("1.0") || version.equals("1.1"); + } else { + return false; } - - return false; } /** diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java index d6d412b..c8819cb 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java @@ -100,6 +100,8 @@ public class DocumentImpl extends InnerNodeImpl implements Document { * @return The new node. */ Node cloneNode(Node node, boolean deep) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_CLONED event + Node target; switch (node.getNodeType()) { @@ -279,6 +281,7 @@ public class DocumentImpl extends InnerNodeImpl implements Document { } public Node importNode(Node importedNode, boolean deep) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_IMPORTED event return cloneNode(importedNode, deep); } @@ -341,6 +344,7 @@ public class DocumentImpl extends InnerNodeImpl implements Document { } public Node adoptNode(Node source) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_ADOPTED event throw new UnsupportedOperationException(); // TODO } @@ -354,6 +358,7 @@ public class DocumentImpl extends InnerNodeImpl implements Document { public Node renameNode(Node n, String namespaceURI, String qualifiedName) throws DOMException { + // TODO: callback the UserDataHandler with a NODE_RENAMED event throw new UnsupportedOperationException(); // TODO } } diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java index 13e6c9f..ebfdd52 100644 --- a/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java +++ b/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java @@ -20,12 +20,16 @@ import org.w3c.dom.Attr; import org.w3c.dom.CharacterData; import org.w3c.dom.DOMException; import org.w3c.dom.Document; +import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.ProcessingInstruction; import org.w3c.dom.UserDataHandler; +import java.util.ArrayList; +import java.util.List; + /** * A straightforward implementation of the corresponding W3C DOM node. * @@ -435,8 +439,11 @@ public abstract class NodeImpl implements Node { return uri.equals(actual); } - public boolean isDefaultNamespace(String namespaceURI) { - throw new UnsupportedOperationException(); // TODO + public final boolean isDefaultNamespace(String namespaceURI) { + String actual = lookupNamespaceURI(null); // null yields the default namespace + return namespaceURI == null + ? actual == null + : namespaceURI.equals(actual); } public final String lookupNamespaceURI(String prefix) { @@ -476,12 +483,108 @@ public abstract class NodeImpl implements Node { return null; } - public boolean isEqualNode(Node arg) { - throw new UnsupportedOperationException(); // TODO + /** + * Returns a list of objects such that two nodes are equal if their lists + * are equal. Be careful: the lists may contain NamedNodeMaps and Nodes, + * neither of which override Object.equals(). Such values must be compared + * manually. + */ + private static List<Object> createEqualityKey(Node node) { + List<Object> values = new ArrayList<Object>(); + values.add(node.getNodeType()); + values.add(node.getNodeName()); + values.add(node.getLocalName()); + values.add(node.getNamespaceURI()); + values.add(node.getPrefix()); + values.add(node.getNodeValue()); + for (Node child = node.getFirstChild(); child != null; child = child.getNextSibling()) { + values.add(child); + } + + switch (node.getNodeType()) { + case DOCUMENT_TYPE_NODE: + DocumentTypeImpl doctype = (DocumentTypeImpl) node; + values.add(doctype.getPublicId()); + values.add(doctype.getSystemId()); + values.add(doctype.getInternalSubset()); + values.add(doctype.getEntities()); + values.add(doctype.getNotations()); + break; + + case ELEMENT_NODE: + Element element = (Element) node; + values.add(element.getAttributes()); + break; + } + + return values; } - public Object getFeature(String feature, String version) { - throw new UnsupportedOperationException(); // TODO + public final boolean isEqualNode(Node arg) { + if (arg == this) { + return true; + } + + List<Object> listA = createEqualityKey(this); + List<Object> listB = createEqualityKey(arg); + + if (listA.size() != listB.size()) { + return false; + } + + for (int i = 0; i < listA.size(); i++) { + Object a = listA.get(i); + Object b = listB.get(i); + + if (a == b) { + continue; + + } else if (a == null || b == null) { + return false; + + } else if (a instanceof String || a instanceof Short) { + if (!a.equals(b)) { + return false; + } + + } else if (a instanceof NamedNodeMap) { + if (!(b instanceof NamedNodeMap) + || !namedNodeMapsEqual((NamedNodeMap) a, (NamedNodeMap) b)) { + return false; + } + + } else if (a instanceof Node) { + if (!(b instanceof Node) + || !((Node) a).isEqualNode((Node) b)) { + return false; + } + + } else { + throw new AssertionError(); // unexpected type + } + } + + return true; + } + + private boolean namedNodeMapsEqual(NamedNodeMap a, NamedNodeMap b) { + if (a.getLength() != b.getLength()) { + return false; + } + for (int i = 0; i < a.getLength(); i++) { + Node aNode = a.item(i); + Node bNode = aNode.getLocalName() == null + ? b.getNamedItem(aNode.getNodeName()) + : b.getNamedItemNS(aNode.getNamespaceURI(), aNode.getLocalName()); + if (bNode == null || !aNode.isEqualNode(bNode)) { + return false; + } + } + return true; + } + + public final Object getFeature(String feature, String version) { + return isSupported(feature, version) ? this : null; } public Object setUserData(String key, Object data, diff --git a/xml/src/test/java/tests/xml/DomTest.java b/xml/src/test/java/tests/xml/DomTest.java index 566b1f0..5f0a19a 100644 --- a/xml/src/test/java/tests/xml/DomTest.java +++ b/xml/src/test/java/tests/xml/DomTest.java @@ -21,6 +21,7 @@ import org.w3c.dom.Attr; import org.w3c.dom.CDATASection; import org.w3c.dom.Comment; import org.w3c.dom.DOMException; +import org.w3c.dom.DOMImplementation; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; import org.w3c.dom.Element; @@ -52,6 +53,7 @@ public class DomTest extends TestCase { private Transformer transformer; private DocumentBuilder builder; + private DOMImplementation domImplementation; private final String xml = "<!DOCTYPE menu [" @@ -106,6 +108,7 @@ public class DomTest extends TestCase { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setNamespaceAware(true); builder = factory.newDocumentBuilder(); + domImplementation = builder.getDOMImplementation(); document = builder.parse(new InputSource(new StringReader(xml))); @@ -319,6 +322,79 @@ public class DomTest extends TestCase { assertEquals("a", vitamincText.lookupPrefix("http://usda")); } + public void testIsDefaultNamespace() { + assertFalse(document.isDefaultNamespace("http://food")); + assertFalse(doctype.isDefaultNamespace("http://food")); + if (sp != null) { + assertFalse(sp.isDefaultNamespace("http://food")); + } + if (png != null) { + assertFalse(png.isDefaultNamespace("http://food")); + } + assertFalse(menu.isDefaultNamespace("http://food")); + assertTrue(item.isDefaultNamespace("http://food")); + assertTrue(itemXmlns.isDefaultNamespace("http://food")); + assertTrue(itemXmlnsA.isDefaultNamespace("http://food")); + assertTrue(name.isDefaultNamespace("http://food")); + assertTrue(standard.isDefaultNamespace("http://food")); + assertTrue(deluxe.isDefaultNamespace("http://food")); + assertFalse(description.isDefaultNamespace("http://food")); + assertFalse(descriptionText1.isDefaultNamespace("http://food")); + assertFalse(descriptionText2.isDefaultNamespace("http://food")); + assertFalse(descriptionText3.isDefaultNamespace("http://food")); + assertTrue(option1.isDefaultNamespace("http://food")); + assertTrue(option2.isDefaultNamespace("http://food")); + assertTrue(option2Reference.isDefaultNamespace("http://food")); + assertTrue(wafflemaker.isDefaultNamespace("http://food")); + assertTrue(nutrition.isDefaultNamespace("http://food")); + assertTrue(vitamins.isDefaultNamespace("http://food")); + assertTrue(vitaminsXmlnsA.isDefaultNamespace("http://food")); + assertTrue(comment.isDefaultNamespace("http://food")); + assertTrue(vitaminc.isDefaultNamespace("http://food")); + assertTrue(vitamincText.isDefaultNamespace("http://food")); + } + + /** + * Xerces fails this test. It returns false always for entity, notation, + * document fragment and document type nodes. This contradicts its own + * behaviour on lookupNamespaceURI(null). + */ + public void testIsDefaultNamespaceNull_XercesBugs() { + String message = "isDefaultNamespace() should be consistent with lookupNamespaceURI(null)"; + assertTrue(message, doctype.isDefaultNamespace(null)); + if (sp != null) { + assertTrue(message, sp.isDefaultNamespace(null)); + } + if (png != null) { + assertTrue(message, png.isDefaultNamespace(null)); + } + } + + public void testIsDefaultNamespaceNull() { + assertTrue(document.isDefaultNamespace(null)); + assertTrue(menu.isDefaultNamespace(null)); + assertFalse(item.isDefaultNamespace(null)); + assertFalse(itemXmlns.isDefaultNamespace(null)); + assertFalse(itemXmlnsA.isDefaultNamespace(null)); + assertFalse(name.isDefaultNamespace(null)); + assertFalse(standard.isDefaultNamespace(null)); + assertFalse(deluxe.isDefaultNamespace(null)); + assertFalse(description.isDefaultNamespace(null)); + assertFalse(descriptionText1.isDefaultNamespace(null)); + assertFalse(descriptionText2.isDefaultNamespace(null)); + assertFalse(descriptionText3.isDefaultNamespace(null)); + assertFalse(option1.isDefaultNamespace(null)); + assertFalse(option2.isDefaultNamespace(null)); + assertFalse(option2Reference.isDefaultNamespace(null)); + assertFalse(wafflemaker.isDefaultNamespace(null)); + assertFalse(nutrition.isDefaultNamespace(null)); + assertFalse(vitamins.isDefaultNamespace(null)); + assertFalse(vitaminsXmlnsA.isDefaultNamespace(null)); + assertFalse(comment.isDefaultNamespace(null)); + assertFalse(vitaminc.isDefaultNamespace(null)); + assertFalse(vitamincText.isDefaultNamespace(null)); + } + public void testDoctypeSetTextContent() throws TransformerException { String original = domToString(document); doctype.setTextContent("foobar"); // strangely, this is specified to no-op @@ -419,6 +495,125 @@ public class DomTest extends TestCase { assertEquals(expected, domToString(document)); } + public void testCoreFeature() { + assertTrue(domImplementation.hasFeature("Core", null)); + assertTrue(domImplementation.hasFeature("Core", "")); + assertTrue(domImplementation.hasFeature("Core", "1.0")); + assertTrue(domImplementation.hasFeature("Core", "2.0")); + assertTrue(domImplementation.hasFeature("Core", "3.0")); + assertTrue(domImplementation.hasFeature("CORE", "3.0")); + assertTrue(domImplementation.hasFeature("+Core", "3.0")); + assertFalse(domImplementation.hasFeature("Core", "4.0")); + } + + public void testXmlFeature() { + assertTrue(domImplementation.hasFeature("XML", null)); + assertTrue(domImplementation.hasFeature("XML", "")); + assertTrue(domImplementation.hasFeature("XML", "1.0")); + assertTrue(domImplementation.hasFeature("XML", "2.0")); + assertTrue(domImplementation.hasFeature("XML", "3.0")); + assertTrue(domImplementation.hasFeature("Xml", "3.0")); + assertTrue(domImplementation.hasFeature("+XML", "3.0")); + assertFalse(domImplementation.hasFeature("XML", "4.0")); + } + + /** + * The RI fails this test. + * http://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Document3-version + */ + public void testXmlVersionFeature() { + String message = "This implementation does not support the XMLVersion feature"; + assertTrue(message, domImplementation.hasFeature("XMLVersion", null)); + assertTrue(message, domImplementation.hasFeature("XMLVersion", "")); + assertTrue(message, domImplementation.hasFeature("XMLVersion", "1.0")); + assertTrue(message, domImplementation.hasFeature("XMLVersion", "1.1")); + assertTrue(message, domImplementation.hasFeature("XMLVERSION", "1.1")); + assertTrue(message, domImplementation.hasFeature("+XMLVersion", "1.1")); + assertFalse(domImplementation.hasFeature("XMLVersion", "1.2")); + assertFalse(domImplementation.hasFeature("XMLVersion", "2.0")); + assertFalse(domImplementation.hasFeature("XMLVersion", "2.0")); + } + + public void testLsFeature() { + assertTrue("This implementation does not support the LS feature", + domImplementation.hasFeature("LS", "3.0")); + } + + public void testElementTraversalFeature() { + assertTrue("This implementation does not support the ElementTraversal feature", + domImplementation.hasFeature("ElementTraversal", "1.0")); + } + + public void testIsSupported() { + // we don't independently test the features; instead just assume the + // implementation calls through to hasFeature (as tested above) + for (Node node : allNodes) { + assertTrue(node.isSupported("XML", null)); + assertTrue(node.isSupported("XML", "3.0")); + assertFalse(node.isSupported("foo", null)); + assertFalse(node.isSupported("foo", "bar")); + } + } + + public void testGetFeature() { + // we don't independently test the features; instead just assume the + // implementation calls through to hasFeature (as tested above) + for (Node node : allNodes) { + assertSame(node, node.getFeature("XML", null)); + assertSame(node, node.getFeature("XML", "3.0")); + assertNull(node.getFeature("foo", null)); + assertNull(node.getFeature("foo", "bar")); + } + } + + public void testNodeEqualsPositive() throws Exception { + DomTest copy = new DomTest(); + copy.setUp(); + + for (int i = 0; i < allNodes.size(); i++) { + Node a = allNodes.get(i); + Node b = copy.allNodes.get(i); + assertTrue(a.isEqualNode(b)); + } + } + + public void testNodeEqualsNegative() throws Exception { + for (Node a : allNodes) { + for (Node b : allNodes) { + assertEquals(a == b, a.isEqualNode(b)); + } + } + } + + public void testNodeEqualsNegativeRecursive() throws Exception { + DomTest copy = new DomTest(); + copy.setUp(); + copy.vitaminc.setTextContent("55%"); + + // changing anything about a node should break equality for all parents + assertFalse(document.isEqualNode(copy.document)); + assertFalse(menu.isEqualNode(copy.menu)); + assertFalse(item.isEqualNode(copy.item)); + assertFalse(nutrition.isEqualNode(copy.nutrition)); + assertFalse(vitamins.isEqualNode(copy.vitamins)); + assertFalse(vitaminc.isEqualNode(copy.vitaminc)); + + // but not siblings + assertTrue(doctype.isEqualNode(copy.doctype)); + assertTrue(description.isEqualNode(copy.description)); + assertTrue(option1.isEqualNode(copy.option1)); + } + + public void testNodeEqualsNull() { + for (Node node : allNodes) { + try { + node.isEqualNode(null); + fail(); + } catch (NullPointerException e) { + } + } + } + private String domToString(Document document) throws TransformerException { StringWriter writer = new StringWriter(); |