From 271825415aa961bdd9f28a551575bcee6f27b4ab Mon Sep 17 00:00:00 2001 From: Jesse Wilson Date: Mon, 15 Mar 2010 16:26:31 -0700 Subject: [PATCH] Implementing Document.renameNode() and DOMImplementation.getFeature(). The rename code required moving some behaviour from ElementImpl and AttrImpl up to their common superclass, NodeImpl. Change-Id: I30910de146f525a5ecc837895ce5808928b858a0 --- .../java/org/apache/harmony/xml/dom/AttrImpl.java | 40 +---- .../harmony/xml/dom/DOMImplementationImpl.java | 2 +- .../org/apache/harmony/xml/dom/DocumentImpl.java | 15 +- .../org/apache/harmony/xml/dom/ElementImpl.java | 33 +--- .../java/org/apache/harmony/xml/dom/NodeImpl.java | 54 +++++- .../harmony/xml/XsltXPathConformanceTestSuite.java | 2 +- libcore/xml/src/test/java/tests/xml/DomTest.java | 182 +++++++++++++++++---- 7 files changed, 228 insertions(+), 100 deletions(-) diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java index 56a4817a7..c601de6ef 100644 --- a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java +++ b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/AttrImpl.java @@ -36,49 +36,19 @@ public final class AttrImpl extends NodeImpl implements Attr { // Maintained by ElementImpl. ElementImpl ownerElement; - - private boolean namespaceAware; - boolean isId; - - private String namespaceURI; - private String localName; + boolean namespaceAware; + String namespaceURI; + String prefix; + String localName; - private String prefix; - private String value; AttrImpl(DocumentImpl document, String namespaceURI, String qualifiedName) { super(document); - namespaceAware = true; - this.namespaceURI = namespaceURI; - - if (qualifiedName == null || "".equals(qualifiedName)) { - throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); - } - - int prefixSeparator = qualifiedName.lastIndexOf(":"); - if (prefixSeparator != -1) { - setPrefix(qualifiedName.substring(0, prefixSeparator)); - qualifiedName = qualifiedName.substring(prefixSeparator + 1); - } - - localName = qualifiedName; - - if ("".equals(localName)) { - throw new DOMException(DOMException.NAMESPACE_ERR, localName); - } - - if ("xmlns".equals(localName) && !"http://www.w3.org/2000/xmlns/".equals(namespaceURI)) { - throw new DOMException(DOMException.NAMESPACE_ERR, localName); - } - - if (!DocumentImpl.isXMLIdentifier(localName)) { - throw new DOMException(DOMException.INVALID_CHARACTER_ERR, localName); - } - + setNameNS(this, namespaceURI, qualifiedName); value = ""; } diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java index b662a13d0..3106c3f38 100644 --- a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java +++ b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DOMImplementationImpl.java @@ -85,6 +85,6 @@ public final class DOMImplementationImpl implements DOMImplementation { } public Object getFeature(String feature, String version) { - throw new UnsupportedOperationException(); // TODO + return hasFeature(feature, version) ? this : null; } } diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java index c677e5849..56283a8d6 100644 --- a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java +++ b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java @@ -299,6 +299,16 @@ public final class DocumentImpl extends InnerNodeImpl implements Document { } } + public Node renameNode(Node node, String namespaceURI, String qualifiedName) { + if (node.getOwnerDocument() != this) { + throw new DOMException(DOMException.WRONG_DOCUMENT_ERR, null); + } + + setNameNS((NodeImpl) node, namespaceURI, qualifiedName); + notifyUserDataHandlers(UserDataHandler.NODE_RENAMED, node, null); + return node; + } + public AttrImpl createAttribute(String name) { return new AttrImpl(this, name); } @@ -467,11 +477,6 @@ public final class DocumentImpl extends InnerNodeImpl implements Document { ((DOMConfigurationImpl) getDomConfig()).normalize(root); } - public Node renameNode(Node n, String namespaceURI, String qualifiedName) { - // TODO: callback the UserDataHandler with a NODE_RENAMED event - throw new UnsupportedOperationException(); // TODO - } - /** * Returns a map with the user data objects attached to the specified node. * This map is readable and writable. diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java index c3e5a2e65..cbc4570bb 100644 --- a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java +++ b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/ElementImpl.java @@ -39,37 +39,16 @@ import java.util.List; */ public class ElementImpl extends InnerNodeImpl implements Element { - private boolean namespaceAware; - - private String namespaceURI; - - private String prefix; - - private String localName; + boolean namespaceAware; + String namespaceURI; + String prefix; + String localName; private List attributes = new ArrayList(); ElementImpl(DocumentImpl document, String namespaceURI, String qualifiedName) { super(document); - - this.namespaceAware = true; - this.namespaceURI = namespaceURI; - - if (qualifiedName == null || "".equals(qualifiedName)) { - throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); - } - - int p = qualifiedName.lastIndexOf(":"); - if (p != -1) { - setPrefix(qualifiedName.substring(0, p)); - qualifiedName = qualifiedName.substring(p + 1); - } - - if (!DocumentImpl.isXMLIdentifier(qualifiedName)) { - throw new DOMException(DOMException.INVALID_CHARACTER_ERR, qualifiedName); - } - - this.localName = qualifiedName; + setNameNS(this, namespaceURI, qualifiedName); } ElementImpl(DocumentImpl document, String name) { @@ -383,7 +362,7 @@ public class ElementImpl extends InnerNodeImpl implements Element { public void setPrefix(String prefix) { this.prefix = validatePrefix(prefix, namespaceAware, namespaceURI); } - + public class ElementAttrNamedNodeMapImpl implements NamedNodeMap { public int getLength() { diff --git a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java index 359a042ed..8beb18cec 100644 --- a/libcore/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java +++ b/libcore/xml/src/main/java/org/apache/harmony/xml/dom/NodeImpl.java @@ -198,7 +198,7 @@ public abstract class NodeImpl implements Node { * @param namespaceAware whether this node is namespace aware * @param namespaceURI this node's namespace URI */ - protected String validatePrefix(String prefix, boolean namespaceAware, String namespaceURI) { + static String validatePrefix(String prefix, boolean namespaceAware, String namespaceURI) { if (!namespaceAware) { throw new DOMException(DOMException.NAMESPACE_ERR, prefix); } @@ -216,6 +216,58 @@ public abstract class NodeImpl implements Node { } /** + * Sets the element or attribute node to be namespace-aware and assign it + * the specified name and namespace URI. + * + * @param node an AttrImpl or ElementImpl node. + * @param namespaceURI this node's namespace URI. May be null. + * @param qualifiedName a possibly-prefixed name like "img" or "html:img". + */ + static void setNameNS(NodeImpl node, String namespaceURI, String qualifiedName) { + if (qualifiedName == null) { + throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); + } + + String prefix = null; + int p = qualifiedName.lastIndexOf(":"); + if (p != -1) { + prefix = validatePrefix(qualifiedName.substring(0, p), true, namespaceURI); + qualifiedName = qualifiedName.substring(p + 1); + } + + if (!DocumentImpl.isXMLIdentifier(qualifiedName)) { + throw new DOMException(DOMException.INVALID_CHARACTER_ERR, qualifiedName); + } + + switch (node.getNodeType()) { + case ATTRIBUTE_NODE: + if ("xmlns".equals(qualifiedName) + && !"http://www.w3.org/2000/xmlns/".equals(namespaceURI)) { + throw new DOMException(DOMException.NAMESPACE_ERR, qualifiedName); + } + + AttrImpl attr = (AttrImpl) node; + attr.namespaceAware = true; + attr.namespaceURI = namespaceURI; + attr.prefix = prefix; + attr.localName = qualifiedName; + break; + + case ELEMENT_NODE: + ElementImpl element = (ElementImpl) node; + element.namespaceAware = true; + element.namespaceURI = namespaceURI; + element.prefix = prefix; + element.localName = qualifiedName; + break; + + default: + throw new DOMException(DOMException.NOT_SUPPORTED_ERR, + "Cannot rename nodes of type " + node.getNodeType()); + } + } + + /** * Checks whether a required string matches an actual string. This utility * method is used for comparing namespaces and such. It takes into account * null arguments and the "*" special case. diff --git a/libcore/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java b/libcore/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java index 7dbfaea9d..ca356d8ee 100644 --- a/libcore/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java +++ b/libcore/xml/src/test/java/org/apache/harmony/xml/XsltXPathConformanceTestSuite.java @@ -74,7 +74,7 @@ import java.util.List; * suite zip file from the OASIS project site. *
  • Unzip. *
  • Copy the files to a device: adb shell mkdir /data/oasis ; - * adb push ./XSLT-Conformance-TC /data/oasis. + * adb push ./XSLT-Conformance-TC/data/oasis. *
  • Invoke this class' main method, passing the on-device path to the test * suite's catalog.xml file as an argument. * diff --git a/libcore/xml/src/test/java/tests/xml/DomTest.java b/libcore/xml/src/test/java/tests/xml/DomTest.java index 3bafb7843..2f364d057 100644 --- a/libcore/xml/src/test/java/tests/xml/DomTest.java +++ b/libcore/xml/src/test/java/tests/xml/DomTest.java @@ -61,6 +61,7 @@ import java.util.regex.Pattern; import static org.w3c.dom.UserDataHandler.NODE_ADOPTED; import static org.w3c.dom.UserDataHandler.NODE_CLONED; import static org.w3c.dom.UserDataHandler.NODE_IMPORTED; +import static org.w3c.dom.UserDataHandler.NODE_RENAMED; /** * Construct a DOM and then interrogate it. @@ -512,25 +513,25 @@ public class DomTest extends TestCase { } 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")); + assertFeature("Core", null); + assertFeature("Core", ""); + assertFeature("Core", "1.0"); + assertFeature("Core", "2.0"); + assertFeature("Core", "3.0"); + assertFeature("CORE", "3.0"); + assertFeature("+Core", "3.0"); + assertNoFeature("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")); + assertFeature("XML", null); + assertFeature("XML", ""); + assertFeature("XML", "1.0"); + assertFeature("XML", "2.0"); + assertFeature("XML", "3.0"); + assertFeature("Xml", "3.0"); + assertFeature("+XML", "3.0"); + assertNoFeature("XML", "4.0"); } /** @@ -538,26 +539,35 @@ public class DomTest extends TestCase { * 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")); + assertFeature("XMLVersion", null); + assertFeature("XMLVersion", ""); + assertFeature("XMLVersion", "1.0"); + assertFeature("XMLVersion", "1.1"); + assertFeature("XMLVERSION", "1.1"); + assertFeature("+XMLVersion", "1.1"); + assertNoFeature("XMLVersion", "1.2"); + assertNoFeature("XMLVersion", "2.0"); + assertNoFeature("XMLVersion", "2.0"); } public void testLsFeature() { - assertTrue("This implementation does not support the LS feature", - domImplementation.hasFeature("LS", "3.0")); + assertFeature("LS", "3.0"); } public void testElementTraversalFeature() { - assertTrue("This implementation does not support the ElementTraversal feature", - domImplementation.hasFeature("ElementTraversal", "1.0")); + assertFeature("ElementTraversal", "1.0"); + } + + private void assertFeature(String feature, String version) { + String message = "This implementation is expected to support " + + feature + " v. " + version + " but does not."; + assertTrue(message, domImplementation.hasFeature(feature, version)); + assertNotNull(message, domImplementation.getFeature(feature, version)); + } + + private void assertNoFeature(String feature, String version) { + assertFalse(domImplementation.hasFeature(feature, version)); + assertNull(domImplementation.getFeature(feature, version)); } public void testIsSupported() { @@ -1189,6 +1199,118 @@ public class DomTest extends TestCase { assertFalse(typeInfo.isDerivedFrom("x", "y", TypeInfo.DERIVATION_UNION)); } + public void testRenameElement() { + document.renameNode(description, null, "desc"); + assertEquals("desc", description.getTagName()); + assertEquals("desc", description.getLocalName()); + assertEquals(null, description.getPrefix()); + assertEquals(null, description.getNamespaceURI()); + } + + public void testRenameElementWithPrefix() { + try { + document.renameNode(description, null, "a:desc"); + fail(); + } catch (DOMException e) { + } + } + + public void testRenameElementWithNamespace() { + document.renameNode(description, "http://sales", "desc"); + assertEquals("desc", description.getTagName()); + assertEquals("desc", description.getLocalName()); + assertEquals(null, description.getPrefix()); + assertEquals("http://sales", description.getNamespaceURI()); + } + + public void testRenameElementWithPrefixAndNamespace() { + document.renameNode(description, "http://sales", "a:desc"); + assertEquals("a:desc", description.getTagName()); + assertEquals("desc", description.getLocalName()); + assertEquals("a", description.getPrefix()); + assertEquals("http://sales", description.getNamespaceURI()); + } + + public void testRenameAttribute() { + document.renameNode(deluxe, null, "special"); + assertEquals("special", deluxe.getName()); + assertEquals("special", deluxe.getLocalName()); + assertEquals(null, deluxe.getPrefix()); + assertEquals(null, deluxe.getNamespaceURI()); + } + + public void testRenameAttributeWithPrefix() { + try { + document.renameNode(deluxe, null, "a:special"); + fail(); + } catch (DOMException e) { + } + } + + public void testRenameAttributeWithNamespace() { + document.renameNode(deluxe, "http://sales", "special"); + assertEquals("special", deluxe.getName()); + assertEquals("special", deluxe.getLocalName()); + assertEquals(null, deluxe.getPrefix()); + assertEquals("http://sales", deluxe.getNamespaceURI()); + } + + public void testRenameAttributeWithPrefixAndNamespace() { + document.renameNode(deluxe, "http://sales", "a:special"); + assertEquals("a:special", deluxe.getName()); + assertEquals("special", deluxe.getLocalName()); + assertEquals("a", deluxe.getPrefix()); + assertEquals("http://sales", deluxe.getNamespaceURI()); + } + + public void testUserDataHandlerNotifiedOfRenames() { + RecordingHandler handler = new RecordingHandler(); + description.setUserData("a", "apple", handler); + deluxe.setUserData("b", "banana", handler); + standard.setUserData("c", "cat", handler); + + document.renameNode(deluxe, null, "special"); + document.renameNode(description, null, "desc"); + + Set expected = new HashSet(); + expected.add(notification(NODE_RENAMED, "a", "apple", description, null)); + expected.add(notification(NODE_RENAMED, "b", "banana", deluxe, null)); + assertEquals(expected, handler.calls); + } + + public void testRenameToInvalid() { + try { + document.renameNode(description, null, "xmlns:foo"); + fail(); + } catch (DOMException e) { + } + try { + document.renameNode(description, null, "xml:foo"); + fail(); + } catch (DOMException e) { + } + try { + document.renameNode(deluxe, null, "xmlns"); + fail(); + } catch (DOMException e) { + } + } + + public void testRenameNodeOtherThanElementOrAttribute() { + for (Node node : allNodes) { + if (node.getNodeType() == Node.ATTRIBUTE_NODE + || node.getNodeType() == Node.ELEMENT_NODE) { + continue; + } + + try { + document.renameNode(node, null, "foo"); + fail(); + } catch (DOMException e) { + } + } + } + private class RecordingHandler implements UserDataHandler { final Set calls = new HashSet(); public void handle(short operation, String key, Object data, Node src, Node dst) { -- 2.11.0