2 * Copyright (C) 2010 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 import dalvik.annotation.KnownFailure;
20 import junit.framework.TestCase;
21 import org.w3c.dom.CDATASection;
22 import org.w3c.dom.Comment;
23 import org.w3c.dom.DOMConfiguration;
24 import org.w3c.dom.DOMError;
25 import org.w3c.dom.DOMErrorHandler;
26 import org.w3c.dom.DOMException;
27 import org.w3c.dom.Document;
28 import org.w3c.dom.Element;
29 import org.w3c.dom.Node;
30 import org.w3c.dom.NodeList;
31 import org.w3c.dom.ProcessingInstruction;
32 import org.w3c.dom.Text;
33 import org.xml.sax.InputSource;
35 import javax.xml.parsers.DocumentBuilderFactory;
36 import javax.xml.transform.OutputKeys;
37 import javax.xml.transform.Transformer;
38 import javax.xml.transform.TransformerException;
39 import javax.xml.transform.TransformerFactory;
40 import javax.xml.transform.dom.DOMSource;
41 import javax.xml.transform.stream.StreamResult;
42 import java.io.StringReader;
43 import java.io.StringWriter;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.Collections;
47 import java.util.List;
50 * Tests the acceptance of various parameters on the DOM configuration. This
51 * test assumes the same set of parameters as the RI version 1.5. Perfectly
52 * correct DOM implementations may fail this test because it assumes certain
53 * parameters will be unsupported.
55 public class NormalizeTest extends TestCase {
57 private Document document;
58 private DOMConfiguration domConfiguration;
60 String[] infosetImpliesFalse = {
61 "validate-if-schema", "entities", "datatype-normalization", "cdata-sections" };
62 String[] infosetImpliesTrue = { "namespace-declarations", "well-formed",
63 "element-content-whitespace", "comments", "namespaces" };
65 @Override protected void setUp() throws Exception {
66 document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
67 domConfiguration = document.getDomConfig();
70 public void testCanonicalForm() {
71 assertEquals(false, domConfiguration.getParameter("canonical-form"));
72 assertSupported("canonical-form", false);
73 assertUnsupported("canonical-form", true);
76 public void testCdataSections() {
77 assertEquals(true, domConfiguration.getParameter("cdata-sections"));
78 assertSupported("cdata-sections", false);
79 assertSupported("cdata-sections", true);
82 public void testCheckCharacterNormalization() {
83 assertEquals(false, domConfiguration.getParameter("check-character-normalization"));
84 assertSupported("check-character-normalization", false);
85 assertUnsupported("check-character-normalization", true);
88 public void testComments() {
89 assertEquals(true, domConfiguration.getParameter("comments"));
90 assertSupported("comments", false);
91 assertSupported("comments", true);
94 public void testDatatypeNormalization() {
95 assertEquals(false, domConfiguration.getParameter("datatype-normalization"));
96 assertSupported("datatype-normalization", false);
97 assertSupported("datatype-normalization", true);
99 // setting this parameter to true should set validate to true...
100 domConfiguration.setParameter("validate", false);
101 domConfiguration.setParameter("datatype-normalization", true);
102 assertEquals(true, domConfiguration.getParameter("validate"));
104 // ...but the negative case isn't so
105 domConfiguration.setParameter("datatype-normalization", false);
106 assertEquals(true, domConfiguration.getParameter("validate"));
109 public void testElementContentWhitespace() {
110 assertEquals(true, domConfiguration.getParameter("element-content-whitespace"));
111 assertUnsupported("element-content-whitespace", false);
112 assertSupported("element-content-whitespace", true);
115 public void testEntities() {
116 assertEquals(true, domConfiguration.getParameter("entities"));
117 assertSupported("entities", false);
118 assertSupported("entities", true);
121 public void testErrorHandler() {
122 assertEquals(null, domConfiguration.getParameter("error-handler"));
123 assertSupported("error-handler", null);
124 assertSupported("error-handler", new DOMErrorHandler() {
125 public boolean handleError(DOMError error) {
131 public void testInfoset() {
132 assertEquals(false, domConfiguration.getParameter("infoset"));
133 assertSupported("infoset", false);
134 assertSupported("infoset", true);
137 public void testSettingInfosetUpdatesImplied() {
138 // first clear those other parameters
139 for (String name : infosetImpliesFalse) {
140 if (domConfiguration.canSetParameter(name, true)) {
141 domConfiguration.setParameter(name, true);
144 for (String name : infosetImpliesTrue) {
145 if (domConfiguration.canSetParameter(name, false)) {
146 domConfiguration.setParameter(name, false);
151 domConfiguration.setParameter("infoset", true);
153 // now the parameters should all match what infoset implies
154 for (String name : infosetImpliesFalse) {
155 assertEquals(false, domConfiguration.getParameter(name));
157 for (String name : infosetImpliesTrue) {
158 assertEquals(true, domConfiguration.getParameter(name));
162 public void testSettingImpliedUpdatesInfoset() {
163 for (String name : infosetImpliesFalse) {
164 domConfiguration.setParameter("infoset", true);
165 if (domConfiguration.canSetParameter(name, true)) {
166 domConfiguration.setParameter(name, true);
167 assertEquals(false, domConfiguration.getParameter("infoset"));
171 for (String name : infosetImpliesTrue) {
172 domConfiguration.setParameter("infoset", true);
173 if (domConfiguration.canSetParameter(name, false)) {
174 domConfiguration.setParameter(name, false);
175 assertEquals(false, domConfiguration.getParameter("infoset"));
180 public void testNamespaces() {
181 assertEquals(true, domConfiguration.getParameter("namespaces"));
182 assertSupported("namespaces", false);
183 assertSupported("namespaces", true);
186 public void testNamespaceDeclarations() {
187 assertEquals(true, domConfiguration.getParameter("namespace-declarations"));
188 assertUnsupported("namespace-declarations", false); // supported in RI 6
189 assertSupported("namespace-declarations", true);
192 public void testNormalizeCharacters() {
193 assertEquals(false, domConfiguration.getParameter("normalize-characters"));
194 assertSupported("normalize-characters", false);
195 assertUnsupported("normalize-characters", true);
198 public void testSchemaLocation() {
199 assertEquals(null, domConfiguration.getParameter("schema-location"));
200 assertSupported("schema-location", "http://foo");
201 assertSupported("schema-location", null);
205 * This fails under the RI because setParameter() succeeds even though
206 * canSetParameter() returns false.
208 @KnownFailure("Dalvik doesn't honor the schema-type parameter")
209 public void testSchemaTypeDtd() {
210 assertUnsupported("schema-type", "http://www.w3.org/TR/REC-xml"); // supported in RI v6
213 public void testSchemaTypeXmlSchema() {
214 assertEquals(null, domConfiguration.getParameter("schema-type"));
215 assertSupported("schema-type", null);
216 assertSupported("schema-type", "http://www.w3.org/2001/XMLSchema");
219 public void testSplitCdataSections() {
220 assertEquals(true, domConfiguration.getParameter("split-cdata-sections"));
221 assertSupported("split-cdata-sections", false);
222 assertSupported("split-cdata-sections", true);
225 public void testValidate() {
226 assertEquals(false, domConfiguration.getParameter("validate"));
227 assertSupported("validate", false);
228 assertSupported("validate", true);
231 public void testValidateIfSchema() {
232 assertEquals(false, domConfiguration.getParameter("validate-if-schema"));
233 assertSupported("validate-if-schema", false);
234 assertUnsupported("validate-if-schema", true);
237 public void testWellFormed() {
238 assertEquals(true, domConfiguration.getParameter("well-formed"));
239 assertSupported("well-formed", false);
240 assertSupported("well-formed", true);
243 public void testMissingParameter() {
244 assertFalse(domConfiguration.canSetParameter("foo", true));
246 domConfiguration.getParameter("foo");
248 } catch (DOMException e) {
251 domConfiguration.setParameter("foo", true);
253 } catch (DOMException e) {
257 public void testNullKey() {
259 domConfiguration.canSetParameter(null, true);
261 } catch (NullPointerException e) {
264 domConfiguration.getParameter(null);
266 } catch (NullPointerException e) {
269 domConfiguration.setParameter(null, true);
271 } catch (NullPointerException e) {
275 public void testNullValue() {
276 String message = "This implementation's canSetParameter() disagrees"
277 + " with its setParameter()";
279 domConfiguration.setParameter("well-formed", null);
281 } catch (DOMException e) {
283 assertEquals(message, false, domConfiguration.canSetParameter("well-formed", null));
286 public void testTypeMismatch() {
287 assertEquals(false, domConfiguration.canSetParameter("well-formed", "true"));
289 domConfiguration.setParameter("well-formed", "true");
291 } catch (DOMException e) {
294 assertEquals(false, domConfiguration.canSetParameter("well-formed", new Object()));
296 domConfiguration.setParameter("well-formed", new Object());
298 } catch (DOMException e) {
302 private void assertUnsupported(String name, Object value) {
303 String message = "This implementation's setParameter() supports an unexpected value: "
304 + name + "=" + value;
305 assertFalse(message, domConfiguration.canSetParameter(name, value));
307 domConfiguration.setParameter(name, value);
309 } catch (DOMException e) {
310 assertEquals(DOMException.NOT_SUPPORTED_ERR, e.code);
313 domConfiguration.setParameter(name.toUpperCase(), value);
315 } catch (DOMException e) {
316 assertEquals(DOMException.NOT_SUPPORTED_ERR, e.code);
318 assertFalse(value.equals(domConfiguration.getParameter(name)));
321 private void assertSupported(String name, Object value) {
322 String message = "This implementation's canSetParameter() disagrees"
323 + " with its setParameter() for " + name + "=" + value;
325 domConfiguration.setParameter(name, value);
326 } catch (DOMException e) {
327 if (domConfiguration.canSetParameter(name, value)) {
330 fail("This implementation's setParameter() doesn't support: "
331 + name + "=" + value);
334 assertTrue(message, domConfiguration.canSetParameter(name.toUpperCase(), value));
335 assertTrue(message, domConfiguration.canSetParameter(name, value));
336 assertEquals(value, domConfiguration.getParameter(name));
337 domConfiguration.setParameter(name.toUpperCase(), value);
338 assertEquals(value, domConfiguration.getParameter(name.toUpperCase()));
341 public void testCdataSectionsNotHonoredByNodeNormalize() throws Exception {
342 String xml = "<foo>ABC<![CDATA[DEF]]>GHI</foo>";
344 domConfiguration.setParameter("cdata-sections", true);
345 document.getDocumentElement().normalize();
346 assertEquals(xml, domToString(document));
349 domConfiguration.setParameter("cdata-sections", false);
350 document.getDocumentElement().normalize();
351 assertEquals(xml, domToString(document));
354 public void testCdataSectionsHonoredByDocumentNormalize() throws Exception {
355 String xml = "<foo>ABC<![CDATA[DEF]]>GHI</foo>";
357 domConfiguration.setParameter("cdata-sections", true);
358 document.normalizeDocument();
359 assertEquals(xml, domToString(document));
362 domConfiguration.setParameter("cdata-sections", false);
363 document.normalizeDocument();
364 String expected = xml.replace("<![CDATA[DEF]]>", "DEF");
365 assertEquals(expected, domToString(document));
368 public void testMergeAdjacentTextNodes() throws Exception {
369 document = createDocumentWithAdjacentTexts("abc", "def");
370 document.getDocumentElement().normalize();
371 assertChildren(document.getDocumentElement(), "abcdef");
374 public void testMergeAdjacentEmptyTextNodes() throws Exception {
375 document = createDocumentWithAdjacentTexts("", "", "");
376 document.getDocumentElement().normalize();
377 assertChildren(document.getDocumentElement());
380 public void testMergeAdjacentNodesWithNonTextSiblings() throws Exception {
381 document = createDocumentWithAdjacentTexts("abc", "def", "<br>", "ghi", "jkl");
382 document.getDocumentElement().normalize();
383 assertChildren(document.getDocumentElement(), "abcdef", "<br>", "ghijkl");
386 public void testMergeAdjacentNodesEliminatesEmptyTexts() throws Exception {
387 document = createDocumentWithAdjacentTexts("", "", "<br>", "", "", "<br>", "", "<br>", "");
388 document.getDocumentElement().normalize();
389 assertChildren(document.getDocumentElement(), "<br>", "<br>", "<br>");
392 public void testRetainingComments() throws Exception {
393 String xml = "<foo>ABC<!-- bar -->DEF<!-- baz -->GHI</foo>";
395 domConfiguration.setParameter("comments", true);
396 document.normalizeDocument();
397 assertEquals(xml, domToString(document));
400 public void testCommentContainingDoubleDash() throws Exception {
401 ErrorRecorder errorRecorder = new ErrorRecorder();
402 domConfiguration.setParameter("error-handler", errorRecorder);
403 domConfiguration.setParameter("namespaces", false);
404 Element root = document.createElement("foo");
405 document.appendChild(root);
406 root.appendChild(document.createComment("ABC -- DEF"));
407 document.normalizeDocument();
408 errorRecorder.assertAllErrors(DOMError.SEVERITY_ERROR, "wf-invalid-character");
411 public void testStrippingComments() throws Exception {
412 String xml = "<foo>ABC<!-- bar -->DEF<!-- baz -->GHI</foo>";
414 domConfiguration.setParameter("comments", false);
415 document.normalizeDocument();
416 assertChildren(document.getDocumentElement(), "ABCDEFGHI");
419 public void testSplittingCdataSectionsSplit() throws Exception {
420 ErrorRecorder errorRecorder = new ErrorRecorder();
421 domConfiguration.setParameter("split-cdata-sections", true);
422 domConfiguration.setParameter("error-handler", errorRecorder);
423 domConfiguration.setParameter("namespaces", false);
424 Element root = document.createElement("foo");
425 document.appendChild(root);
426 root.appendChild(document.createCDATASection("ABC]]>DEF]]>GHI"));
427 document.normalizeDocument();
428 errorRecorder.assertAllErrors(DOMError.SEVERITY_WARNING, "cdata-sections-splitted");
429 assertChildren(root, "<![CDATA[ABC]]]]>", "<![CDATA[>DEF]]]]>", "<![CDATA[>GHI]]>");
432 public void testSplittingCdataSectionsReportError() throws Exception {
433 ErrorRecorder errorRecorder = new ErrorRecorder();
434 domConfiguration.setParameter("split-cdata-sections", false);
435 domConfiguration.setParameter("error-handler", errorRecorder);
436 domConfiguration.setParameter("namespaces", false);
437 Element root = document.createElement("foo");
438 document.appendChild(root);
439 root.appendChild(document.createCDATASection("ABC]]>DEF"));
440 document.normalizeDocument();
441 errorRecorder.assertAllErrors(DOMError.SEVERITY_ERROR, "wf-invalid-character");
444 public void testInvalidCharactersCdata() throws Exception {
445 ErrorRecorder errorRecorder = new ErrorRecorder();
446 domConfiguration.setParameter("cdata-sections", true);
447 domConfiguration.setParameter("error-handler", errorRecorder);
448 domConfiguration.setParameter("namespaces", false);
449 Element root = document.createElement("foo");
450 document.appendChild(root);
451 CDATASection cdata = document.createCDATASection("");
452 root.appendChild(cdata);
454 for (int c = 0; c <= Character.MAX_VALUE; c++) {
455 cdata.setData(new String(new char[]{ 'A', 'B', (char) c }));
456 document.normalizeDocument();
457 if (isValid((char) c)) {
458 assertEquals(Collections.<DOMError>emptyList(), errorRecorder.errors);
460 errorRecorder.assertAllErrors("For character " + c,
461 DOMError.SEVERITY_ERROR, "wf-invalid-character");
466 public void testInvalidCharactersText() throws Exception {
467 ErrorRecorder errorRecorder = new ErrorRecorder();
468 domConfiguration.setParameter("error-handler", errorRecorder);
469 domConfiguration.setParameter("namespaces", false);
470 Element root = document.createElement("foo");
471 document.appendChild(root);
472 Text text = document.createTextNode("");
473 root.appendChild(text);
475 for (int c = 0; c <= Character.MAX_VALUE; c++) {
476 text.setData(new String(new char[]{ 'A', 'B', (char) c }));
477 document.normalizeDocument();
478 if (isValid((char) c)) {
479 assertEquals(Collections.<DOMError>emptyList(), errorRecorder.errors);
481 errorRecorder.assertAllErrors("For character " + c,
482 DOMError.SEVERITY_ERROR, "wf-invalid-character");
487 public void testInvalidCharactersAttribute() throws Exception {
488 ErrorRecorder errorRecorder = new ErrorRecorder();
489 domConfiguration.setParameter("error-handler", errorRecorder);
490 domConfiguration.setParameter("namespaces", false);
491 Element root = document.createElement("foo");
492 document.appendChild(root);
494 for (int c = 0; c <= Character.MAX_VALUE; c++) {
495 root.setAttribute("bar", new String(new char[] { 'A', 'B', (char) c}));
496 document.normalizeDocument();
497 if (isValid((char) c)) {
498 assertEquals(Collections.<DOMError>emptyList(), errorRecorder.errors);
500 errorRecorder.assertAllErrors("For character " + c,
501 DOMError.SEVERITY_ERROR, "wf-invalid-character");
506 public void testInvalidCharactersComment() throws Exception {
507 ErrorRecorder errorRecorder = new ErrorRecorder();
508 domConfiguration.setParameter("error-handler", errorRecorder);
509 domConfiguration.setParameter("namespaces", false);
510 Element root = document.createElement("foo");
511 document.appendChild(root);
512 Comment comment = document.createComment("");
513 root.appendChild(comment);
515 for (int c = 0; c <= Character.MAX_VALUE; c++) {
516 comment.setData(new String(new char[] { 'A', 'B', (char) c}));
517 document.normalizeDocument();
518 if (isValid((char) c)) {
519 assertEquals(Collections.<DOMError>emptyList(), errorRecorder.errors);
521 errorRecorder.assertAllErrors("For character " + c,
522 DOMError.SEVERITY_ERROR, "wf-invalid-character");
527 public void testInvalidCharactersProcessingInstructionData() throws Exception {
528 ErrorRecorder errorRecorder = new ErrorRecorder();
529 domConfiguration.setParameter("error-handler", errorRecorder);
530 domConfiguration.setParameter("namespaces", false);
531 Element root = document.createElement("foo");
532 document.appendChild(root);
533 ProcessingInstruction pi = document.createProcessingInstruction("foo", "");
534 root.appendChild(pi);
536 for (int c = 0; c <= Character.MAX_VALUE; c++) {
537 pi.setData(new String(new char[] { 'A', 'B', (char) c}));
538 document.normalizeDocument();
539 if (isValid((char) c)) {
540 assertEquals(Collections.<DOMError>emptyList(), errorRecorder.errors);
542 errorRecorder.assertAllErrors("For character " + c,
543 DOMError.SEVERITY_ERROR, "wf-invalid-character");
548 // TODO: test for surrogates
550 private boolean isValid(char c) {
551 // as defined by http://www.w3.org/TR/REC-xml/#charsets.
552 return c == 0x9 || c == 0xA || c == 0xD || (c >= 0x20 && c <= 0xd7ff)
553 || (c >= 0xe000 && c <= 0xfffd);
556 private Document createDocumentWithAdjacentTexts(String... texts) throws Exception {
557 Document result = DocumentBuilderFactory.newInstance()
558 .newDocumentBuilder().newDocument();
559 Element root = result.createElement("foo");
560 result.appendChild(root);
561 for (String text : texts) {
562 if (text.equals("<br>")) {
563 root.appendChild(result.createElement("br"));
565 root.appendChild(result.createTextNode(text));
571 private void assertChildren(Element element, String... texts) {
572 List<String> actual = new ArrayList<String>();
573 NodeList nodes = element.getChildNodes();
574 for (int i = 0; i < nodes.getLength(); i++) {
575 Node node = nodes.item(i);
576 if (node.getNodeType() == Node.TEXT_NODE) {
577 actual.add(((Text) node).getData());
578 } else if (node.getNodeType() == Node.CDATA_SECTION_NODE) {
579 actual.add("<![CDATA[" + ((CDATASection) node).getData() + "]]>");
581 actual.add("<" + node.getNodeName() + ">");
584 assertEquals(Arrays.asList(texts), actual);
587 private void parse(String xml) throws Exception {
588 document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
589 .parse(new InputSource(new StringReader(xml)));
590 domConfiguration = document.getDomConfig();
593 private String domToString(Document document) throws TransformerException {
594 StringWriter writer = new StringWriter();
595 Transformer transformer = TransformerFactory.newInstance() .newTransformer();
596 transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
597 transformer.transform(new DOMSource(document), new StreamResult(writer));
598 return writer.toString();
601 private class ErrorRecorder implements DOMErrorHandler {
602 private final List<DOMError> errors = new ArrayList<DOMError>();
604 public boolean handleError(DOMError error) {
609 public void assertAllErrors(int severity, String type) {
610 assertAllErrors("Expected one or more " + type + " errors", severity, type);
613 public void assertAllErrors(String message, int severity, String type) {
614 assertFalse(message, errors.isEmpty());
615 for (DOMError error : errors) {
616 assertEquals(message, severity, error.getSeverity());
617 assertEquals(message, type, error.getType());