summaryrefslogtreecommitdiffstats
path: root/xml/src/main
diff options
context:
space:
mode:
authorJesse Wilson <jessewilson@google.com>2010-02-27 00:00:54 -0800
committerJesse Wilson <jessewilson@google.com>2010-03-01 10:20:23 -0800
commit81ca3dd430f9c038b4257e1124f45a07d0e6b543 (patch)
tree83a73c2eb26629f46f1202867dfde4c5d5efb46b /xml/src/main
parent6781ce008276a8a5af7c0ebec2de43dee9b2ad29 (diff)
downloadlibcore-81ca3dd430f9c038b4257e1124f45a07d0e6b543.zip
libcore-81ca3dd430f9c038b4257e1124f45a07d0e6b543.tar.gz
libcore-81ca3dd430f9c038b4257e1124f45a07d0e6b543.tar.bz2
New implementation for DOMConfiguration.
This API is disgusting. Its not regular, not typesafe, sparsely implemented and overly specific. I'm implementing the minimum I can get away with - exactly the same route taken by the RI. The Parameter stuff feels a little bit like overkill, but it allowed me to group related chunks of code together and avoid long chains of equalsIgnoreCase if statements. This is necessary to implement Document.normalize(), one of the last remaining APIs in DOMv3. Also fixing our implementation of Node.normalize() and adding tests to discover a bug in that code. Previously a document like this: "<foo>abc<br>def</foo>" Would be normalized to this: "<foo>abcdef<br></foo>" Which is a horrible bug! Yuck. Implementation and tests for Document.normalize() are forthcoming.
Diffstat (limited to 'xml/src/main')
-rw-r--r--xml/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java371
-rw-r--r--xml/src/main/java/org/apache/harmony/xml/dom/DocumentImpl.java6
-rw-r--r--xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java45
3 files changed, 402 insertions, 20 deletions
diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java
new file mode 100644
index 0000000..2f57a4c
--- /dev/null
+++ b/xml/src/main/java/org/apache/harmony/xml/dom/DOMConfigurationImpl.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.harmony.xml.dom;
+
+import org.w3c.dom.DOMConfiguration;
+import org.w3c.dom.DOMErrorHandler;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.DOMStringList;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A minimal implementation of DOMConfiguration. This implementation uses inner
+ * parameter instances to centralize each parameter's behaviour.
+ */
+public final class DOMConfigurationImpl implements DOMConfiguration {
+
+ private static final Map<String, Parameter> PARAMETERS
+ = new TreeMap<String, Parameter>(String.CASE_INSENSITIVE_ORDER);
+
+ static {
+ /*
+ * True to canonicalize the document (unsupported). This includes
+ * removing DocumentType nodes from the tree and removing unused
+ * namespace declarations. Setting this to true also sets these
+ * parameters:
+ * entities = false
+ * normalize-characters = false
+ * cdata-sections = false
+ * namespaces = true
+ * namespace-declarations = true
+ * well-formed = true
+ * element-content-whitespace = true
+ * Setting these parameters to another value shall revert the canonical
+ * form to false.
+ */
+ PARAMETERS.put("canonical-form", new FixedParameter(false));
+
+ /*
+ * True to keep existing CDATA nodes; false to replace them/merge them
+ * into adjacent text nodes.
+ */
+ PARAMETERS.put("cdata-sections", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.cdataSections;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.cdataSections = (Boolean) value;
+ }
+ });
+
+ /*
+ * True to check character normalization (unsupported).
+ */
+ PARAMETERS.put("check-character-normalization", new FixedParameter(false));
+
+ /*
+ * True to keep comments in the document; false to discard them.
+ */
+ PARAMETERS.put("comments", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.comments;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.comments = (Boolean) value;
+ }
+ });
+
+ /*
+ * True to expose schema normalized values. Setting this to true sets
+ * the validate parameter to true. Has no effect when validate is false.
+ */
+ PARAMETERS.put("datatype-normalization", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.datatypeNormalization;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ if ((Boolean) value) {
+ config.datatypeNormalization = true;
+ config.validate = true;
+ } else {
+ config.datatypeNormalization = false;
+ }
+ }
+ });
+
+ /*
+ * True to keep whitespace elements in the document; false to discard
+ * them (unsupported).
+ */
+ PARAMETERS.put("element-content-whitespace", new FixedParameter(true));
+
+ /*
+ * True to keep entity references in the document; false to expand them.
+ */
+ PARAMETERS.put("entities", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.entities;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.entities = (Boolean) value;
+ }
+ });
+
+ /*
+ * Handler to be invoked when errors are encountered.
+ */
+ PARAMETERS.put("error-handler", new Parameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.errorHandler;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.errorHandler = (DOMErrorHandler) value;
+ }
+ public boolean canSet(DOMConfigurationImpl config, Object value) {
+ return value == null || value instanceof DOMErrorHandler;
+ }
+ });
+
+ /*
+ * Bulk alias to set the following parameter values:
+ * validate-if-schema = false
+ * entities = false
+ * datatype-normalization = false
+ * cdata-sections = false
+ * namespace-declarations = true
+ * well-formed = true
+ * element-content-whitespace = true
+ * comments = true
+ * namespaces = true.
+ * Querying this returns true if all of the above parameters have the
+ * listed values; false otherwise.
+ */
+ PARAMETERS.put("infoset", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ // validate-if-schema is always false
+ // element-content-whitespace is always true
+ // namespace-declarations is always true
+ return !config.entities
+ && !config.datatypeNormalization
+ && !config.cdataSections
+ && config.wellFormed
+ && config.comments
+ && config.namespaces;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ if ((Boolean) value) {
+ // validate-if-schema is always false
+ // element-content-whitespace is always true
+ // namespace-declarations is always true
+ config.entities = false;
+ config.datatypeNormalization = false;
+ config.cdataSections = false;
+ config.wellFormed = true;
+ config.comments = true;
+ config.namespaces = true;
+ }
+ }
+ });
+
+ /*
+ * True to perform namespace processing; false for none.
+ */
+ PARAMETERS.put("namespaces", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.namespaces;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.namespaces = (Boolean) value;
+ }
+ });
+
+ /**
+ * True to include namespace declarations; false to discard them
+ * (unsupported). Even when namespace declarations are discarded,
+ * prefixes are retained.
+ *
+ * Has no effect if namespaces is false.
+ */
+ PARAMETERS.put("namespace-declarations", new FixedParameter(true));
+
+ /*
+ * True to fully normalize characters (unsupported).
+ */
+ PARAMETERS.put("normalize-characters", new FixedParameter(false));
+
+ /*
+ * A list of whitespace-separated URIs representing the schemas to validate
+ * against. Has no effect if schema-type is null.
+ */
+ PARAMETERS.put("schema-location", new Parameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.schemaLocation;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.schemaLocation = (String) value;
+ }
+ public boolean canSet(DOMConfigurationImpl config, Object value) {
+ return value == null || value instanceof String;
+ }
+ });
+
+ /*
+ * URI representing the type of schema language, such as
+ * "http://www.w3.org/2001/XMLSchema" or "http://www.w3.org/TR/REC-xml".
+ */
+ PARAMETERS.put("schema-type", new Parameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.schemaType;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.schemaType = (String) value;
+ }
+ public boolean canSet(DOMConfigurationImpl config, Object value) {
+ return value == null || value instanceof String;
+ }
+ });
+
+ /*
+ * True to split CDATA sections containing "]]>"; false to signal an
+ * error instead.
+ */
+ PARAMETERS.put("split-cdata-sections", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.splitCdataSections;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.splitCdataSections = (Boolean) value;
+ }
+ });
+
+ /*
+ * True to require validation against a schema or DTD. Validation will
+ * recompute element content whitespace, ID and schema type data.
+ *
+ * Setting this unsets validate-if-schema.
+ */
+ PARAMETERS.put("validate", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.validate;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ // validate-if-schema is always false
+ config.validate = (Boolean) value;
+ }
+ });
+
+ /*
+ * True to validate if a schema was declared (unsupported). Setting this
+ * unsets validate.
+ */
+ PARAMETERS.put("validate-if-schema", new FixedParameter(false));
+
+ /*
+ * True to report invalid characters in node names, attributes, elements,
+ * comments, text, CDATA sections and processing instructions.
+ */
+ PARAMETERS.put("well-formed", new BooleanParameter() {
+ public Object get(DOMConfigurationImpl config) {
+ return config.wellFormed;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ config.wellFormed = (Boolean) value;
+ }
+ });
+
+ // TODO add "resource-resolver" property for use with LS feature...
+ }
+
+ private boolean cdataSections = true;
+ private boolean comments = true;
+ private boolean datatypeNormalization = false;
+ private boolean entities = true;
+ private DOMErrorHandler errorHandler;
+ private boolean namespaces = true;
+ private String schemaLocation;
+ private String schemaType;
+ private boolean splitCdataSections = true;
+ private boolean validate = false;
+ private boolean wellFormed = true;
+
+ interface Parameter {
+ Object get(DOMConfigurationImpl config);
+ void set(DOMConfigurationImpl config, Object value);
+ boolean canSet(DOMConfigurationImpl config, Object value);
+ }
+
+ static class FixedParameter implements Parameter {
+ final Object onlyValue;
+ FixedParameter(Object onlyValue) {
+ this.onlyValue = onlyValue;
+ }
+ public Object get(DOMConfigurationImpl config) {
+ return onlyValue;
+ }
+ public void set(DOMConfigurationImpl config, Object value) {
+ if (!onlyValue.equals(value)) {
+ throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
+ "Unsupported value: " + value);
+ }
+ }
+ public boolean canSet(DOMConfigurationImpl config, Object value) {
+ return onlyValue.equals(value);
+ }
+ }
+
+ static abstract class BooleanParameter implements Parameter {
+ public boolean canSet(DOMConfigurationImpl config, Object value) {
+ return value instanceof Boolean;
+ }
+ }
+
+ public boolean canSetParameter(String name, Object value) {
+ Parameter parameter = PARAMETERS.get(name);
+ return parameter != null && parameter.canSet(this, value);
+ }
+
+ public void setParameter(String name, Object value) throws DOMException {
+ Parameter parameter = PARAMETERS.get(name);
+ if (parameter == null) {
+ throw new DOMException(DOMException.NOT_FOUND_ERR, "No such parameter: " + name);
+ }
+ try {
+ parameter.set(this, value);
+ } catch (NullPointerException e) {
+ throw new DOMException(DOMException.TYPE_MISMATCH_ERR,
+ "Null not allowed for " + name);
+ } catch (ClassCastException e) {
+ throw new DOMException(DOMException.TYPE_MISMATCH_ERR,
+ "Invalid type for " + name + ": " + value.getClass());
+ }
+ }
+
+ public Object getParameter(String name) throws DOMException {
+ Parameter parameter = PARAMETERS.get(name);
+ if (parameter == null) {
+ throw new DOMException(DOMException.NOT_FOUND_ERR, "No such parameter: " + name);
+ }
+ return parameter.get(this);
+ }
+
+ public DOMStringList getParameterNames() {
+ final String[] result = PARAMETERS.keySet().toArray(new String[PARAMETERS.size()]);
+ return new DOMStringList() {
+ public String item(int index) {
+ return index < result.length ? result[index] : null;
+ }
+ public int getLength() {
+ return result.length;
+ }
+ public boolean contains(String str) {
+ return PARAMETERS.containsKey(str); // case-insensitive.
+ }
+ };
+ }
+}
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 e297280..035e1bb 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
@@ -47,6 +47,7 @@ import org.w3c.dom.Text;
public class DocumentImpl extends InnerNodeImpl implements Document {
private DOMImplementation domImplementation;
+ private DOMConfiguration domConfiguration;
/*
* The default values of these fields are specified by the Document
@@ -361,7 +362,10 @@ public class DocumentImpl extends InnerNodeImpl implements Document {
}
public DOMConfiguration getDomConfig() {
- throw new UnsupportedOperationException(); // TODO
+ if (domConfiguration == null) {
+ domConfiguration = new DOMConfigurationImpl();
+ }
+ return domConfiguration;
}
public void normalizeDocument() {
diff --git a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java
index 275bbf3..9cee352 100644
--- a/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java
+++ b/xml/src/main/java/org/apache/harmony/xml/dom/InnerNodeImpl.java
@@ -148,28 +148,35 @@ public abstract class InnerNodeImpl extends LeafNodeImpl {
return false;
}
+ /**
+ * Normalize the text nodes within this subtree. Although named similarly,
+ * this method is unrelated to Document.normalize.
+ */
@Override
- public void normalize() {
- Node nextNode = null;
-
+ public final void normalize() {
+ Text next = null; // null if next doesn't exist or is not a TEXT_NODE
for (int i = children.size() - 1; i >= 0; i--) {
- Node thisNode = children.get(i);
-
- thisNode.normalize();
-
- if (thisNode.getNodeType() == Node.TEXT_NODE) {
- if (nextNode != null && nextNode.getNodeType() == Node.TEXT_NODE) {
- ((Text)thisNode).setData(thisNode.getNodeValue() + nextNode.getNodeValue());
- removeChild(nextNode);
- }
-
- if ("".equals(thisNode.getNodeValue())) {
- removeChild(thisNode);
- nextNode = null;
- } else {
- nextNode = thisNode;
- }
+ Node node = children.get(i);
+ node.normalize();
+
+ if (node.getNodeType() != Node.TEXT_NODE) {
+ next = null;
+ continue;
}
+
+ Text text = (Text) node;
+
+ if (text.getLength() == 0) {
+ removeChild(text);
+ continue;
+ }
+
+ if (next != null) {
+ text.appendData(next.getData());
+ removeChild(next);
+ }
+
+ next = text;
}
}