OSDN Git Service

original
[gb-231r1-is01/Gingerbread_2.3.3_r1_IS01.git] / libcore / luni / src / test / java / tests / xml / NormalizeTest.java
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package tests.xml;
18
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;
34
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;
48
49 /**
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.
54  */
55 public class NormalizeTest extends TestCase {
56
57     private Document document;
58     private DOMConfiguration domConfiguration;
59
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" };
64
65     @Override protected void setUp() throws Exception {
66         document = DocumentBuilderFactory.newInstance().newDocumentBuilder().newDocument();
67         domConfiguration = document.getDomConfig();
68     }
69
70     public void testCanonicalForm() {
71         assertEquals(false, domConfiguration.getParameter("canonical-form"));
72         assertSupported("canonical-form", false);
73         assertUnsupported("canonical-form", true);
74     }
75
76     public void testCdataSections() {
77         assertEquals(true, domConfiguration.getParameter("cdata-sections"));
78         assertSupported("cdata-sections", false);
79         assertSupported("cdata-sections", true);
80     }
81
82     public void testCheckCharacterNormalization() {
83         assertEquals(false, domConfiguration.getParameter("check-character-normalization"));
84         assertSupported("check-character-normalization", false);
85         assertUnsupported("check-character-normalization", true);
86     }
87
88     public void testComments() {
89         assertEquals(true, domConfiguration.getParameter("comments"));
90         assertSupported("comments", false);
91         assertSupported("comments", true);
92     }
93
94     public void testDatatypeNormalization() {
95         assertEquals(false, domConfiguration.getParameter("datatype-normalization"));
96         assertSupported("datatype-normalization", false);
97         assertSupported("datatype-normalization", true);
98
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"));
103
104         // ...but the negative case isn't so
105         domConfiguration.setParameter("datatype-normalization", false);
106         assertEquals(true, domConfiguration.getParameter("validate"));
107     }
108
109     public void testElementContentWhitespace() {
110         assertEquals(true, domConfiguration.getParameter("element-content-whitespace"));
111         assertUnsupported("element-content-whitespace", false);
112         assertSupported("element-content-whitespace", true);
113     }
114
115     public void testEntities() {
116         assertEquals(true, domConfiguration.getParameter("entities"));
117         assertSupported("entities", false);
118         assertSupported("entities", true);
119     }
120
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) {
126                 return true;
127             }
128         });
129     }
130
131     public void testInfoset() {
132         assertEquals(false, domConfiguration.getParameter("infoset"));
133         assertSupported("infoset", false);
134         assertSupported("infoset", true);
135     }
136
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);
142             }
143         }
144         for (String name : infosetImpliesTrue) {
145             if (domConfiguration.canSetParameter(name, false)) {
146                 domConfiguration.setParameter(name, false);
147             }
148         }
149
150         // set infoset
151         domConfiguration.setParameter("infoset", true);
152
153         // now the parameters should all match what infoset implies
154         for (String name : infosetImpliesFalse) {
155             assertEquals(false, domConfiguration.getParameter(name));
156         }
157         for (String name : infosetImpliesTrue) {
158             assertEquals(true, domConfiguration.getParameter(name));
159         }
160     }
161
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"));
168             }
169         }
170
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"));
176             }
177         }
178     }
179
180     public void testNamespaces() {
181         assertEquals(true, domConfiguration.getParameter("namespaces"));
182         assertSupported("namespaces", false);
183         assertSupported("namespaces", true);
184     }
185
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);
190     }
191
192     public void testNormalizeCharacters() {
193         assertEquals(false, domConfiguration.getParameter("normalize-characters"));
194         assertSupported("normalize-characters", false);
195         assertUnsupported("normalize-characters", true);
196     }
197
198     public void testSchemaLocation() {
199         assertEquals(null, domConfiguration.getParameter("schema-location"));
200         assertSupported("schema-location", "http://foo");
201         assertSupported("schema-location", null);
202     }
203
204     /**
205      * This fails under the RI because setParameter() succeeds even though
206      * canSetParameter() returns false.
207      */
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
211     }
212
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");
217     }
218
219     public void testSplitCdataSections() {
220         assertEquals(true, domConfiguration.getParameter("split-cdata-sections"));
221         assertSupported("split-cdata-sections", false);
222         assertSupported("split-cdata-sections", true);
223     }
224
225     public void testValidate() {
226         assertEquals(false, domConfiguration.getParameter("validate"));
227         assertSupported("validate", false);
228         assertSupported("validate", true);
229     }
230
231     public void testValidateIfSchema() {
232         assertEquals(false, domConfiguration.getParameter("validate-if-schema"));
233         assertSupported("validate-if-schema", false);
234         assertUnsupported("validate-if-schema", true);
235     }
236
237     public void testWellFormed() {
238         assertEquals(true, domConfiguration.getParameter("well-formed"));
239         assertSupported("well-formed", false);
240         assertSupported("well-formed", true);
241     }
242
243     public void testMissingParameter() {
244         assertFalse(domConfiguration.canSetParameter("foo", true));
245         try {
246             domConfiguration.getParameter("foo");
247             fail();
248         } catch (DOMException e) {
249         }
250         try {
251             domConfiguration.setParameter("foo", true);
252             fail();
253         } catch (DOMException e) {
254         }
255     }
256
257     public void testNullKey() {
258         try {
259             domConfiguration.canSetParameter(null, true);
260             fail();
261         } catch (NullPointerException e) {
262         }
263         try {
264             domConfiguration.getParameter(null);
265             fail();
266         } catch (NullPointerException e) {
267         }
268         try {
269             domConfiguration.setParameter(null, true);
270             fail();
271         } catch (NullPointerException e) {
272         }
273     }
274
275     public void testNullValue() {
276         String message = "This implementation's canSetParameter() disagrees"
277                 + " with its setParameter()";
278         try {
279             domConfiguration.setParameter("well-formed", null);
280             fail(message);
281         } catch (DOMException e) {
282         }
283         assertEquals(message, false, domConfiguration.canSetParameter("well-formed", null));
284     }
285
286     public void testTypeMismatch() {
287         assertEquals(false, domConfiguration.canSetParameter("well-formed", "true"));
288         try {
289             domConfiguration.setParameter("well-formed", "true");
290             fail();
291         } catch (DOMException e) {
292         }
293
294         assertEquals(false, domConfiguration.canSetParameter("well-formed", new Object()));
295         try {
296             domConfiguration.setParameter("well-formed", new Object());
297             fail();
298         } catch (DOMException e) {
299         }
300     }
301
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));
306         try {
307             domConfiguration.setParameter(name, value);
308             fail(message);
309         } catch (DOMException e) {
310             assertEquals(DOMException.NOT_SUPPORTED_ERR, e.code);
311         }
312         try {
313             domConfiguration.setParameter(name.toUpperCase(), value);
314             fail(message);
315         } catch (DOMException e) {
316             assertEquals(DOMException.NOT_SUPPORTED_ERR, e.code);
317         }
318         assertFalse(value.equals(domConfiguration.getParameter(name)));
319     }
320
321     private void assertSupported(String name, Object value) {
322         String message = "This implementation's canSetParameter() disagrees"
323                 + " with its setParameter() for " + name + "=" + value;
324         try {
325             domConfiguration.setParameter(name, value);
326         } catch (DOMException e) {
327             if (domConfiguration.canSetParameter(name, value)) {
328                 fail(message);
329             } else {
330                 fail("This implementation's setParameter() doesn't support: "
331                         + name + "=" + value);
332             }
333         }
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()));
339     }
340
341     public void testCdataSectionsNotHonoredByNodeNormalize() throws Exception {
342         String xml = "<foo>ABC<![CDATA[DEF]]>GHI</foo>";
343         parse(xml);
344         domConfiguration.setParameter("cdata-sections", true);
345         document.getDocumentElement().normalize();
346         assertEquals(xml, domToString(document));
347
348         parse(xml);
349         domConfiguration.setParameter("cdata-sections", false);
350         document.getDocumentElement().normalize();
351         assertEquals(xml, domToString(document));
352     }
353
354     public void testCdataSectionsHonoredByDocumentNormalize() throws Exception {
355         String xml = "<foo>ABC<![CDATA[DEF]]>GHI</foo>";
356         parse(xml);
357         domConfiguration.setParameter("cdata-sections", true);
358         document.normalizeDocument();
359         assertEquals(xml, domToString(document));
360
361         parse(xml);
362         domConfiguration.setParameter("cdata-sections", false);
363         document.normalizeDocument();
364         String expected = xml.replace("<![CDATA[DEF]]>", "DEF");
365         assertEquals(expected, domToString(document));
366     }
367
368     public void testMergeAdjacentTextNodes() throws Exception {
369         document = createDocumentWithAdjacentTexts("abc", "def");
370         document.getDocumentElement().normalize();
371         assertChildren(document.getDocumentElement(), "abcdef");
372     }
373
374     public void testMergeAdjacentEmptyTextNodes() throws Exception {
375         document = createDocumentWithAdjacentTexts("", "", "");
376         document.getDocumentElement().normalize();
377         assertChildren(document.getDocumentElement());
378     }
379
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");
384     }
385
386     public void testMergeAdjacentNodesEliminatesEmptyTexts() throws Exception {
387         document = createDocumentWithAdjacentTexts("", "", "<br>", "", "", "<br>", "", "<br>", "");
388         document.getDocumentElement().normalize();
389         assertChildren(document.getDocumentElement(), "<br>", "<br>", "<br>");
390     }
391
392     public void testRetainingComments() throws Exception {
393         String xml = "<foo>ABC<!-- bar -->DEF<!-- baz -->GHI</foo>";
394         parse(xml);
395         domConfiguration.setParameter("comments", true);
396         document.normalizeDocument();
397         assertEquals(xml, domToString(document));
398     }
399
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");
409     }
410
411     public void testStrippingComments() throws Exception {
412         String xml = "<foo>ABC<!-- bar -->DEF<!-- baz -->GHI</foo>";
413         parse(xml);
414         domConfiguration.setParameter("comments", false);
415         document.normalizeDocument();
416         assertChildren(document.getDocumentElement(), "ABCDEFGHI");
417     }
418
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]]>");
430     }
431
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");
442     }
443
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);
453
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);
459             } else {
460                 errorRecorder.assertAllErrors("For character " + c,
461                         DOMError.SEVERITY_ERROR, "wf-invalid-character");
462             }
463         }
464     }
465
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);
474
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);
480             } else {
481                 errorRecorder.assertAllErrors("For character " + c,
482                         DOMError.SEVERITY_ERROR, "wf-invalid-character");
483             }
484         }
485     }
486
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);
493
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);
499             } else {
500                 errorRecorder.assertAllErrors("For character " + c,
501                         DOMError.SEVERITY_ERROR, "wf-invalid-character");
502             }
503         }
504     }
505
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);
514
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);
520             } else {
521                 errorRecorder.assertAllErrors("For character " + c,
522                         DOMError.SEVERITY_ERROR, "wf-invalid-character");
523             }
524         }
525     }
526
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);
535
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);
541             } else {
542                 errorRecorder.assertAllErrors("For character " + c,
543                         DOMError.SEVERITY_ERROR, "wf-invalid-character");
544             }
545         }
546     }
547
548     // TODO: test for surrogates
549
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);
554     }
555
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"));
564             } else {
565                 root.appendChild(result.createTextNode(text));
566             }
567         }
568         return result;
569     }
570
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() + "]]>");
580             } else {
581                 actual.add("<" + node.getNodeName() + ">");
582             }
583         }
584         assertEquals(Arrays.asList(texts), actual);
585     }
586
587     private void parse(String xml) throws Exception {
588         document = DocumentBuilderFactory.newInstance().newDocumentBuilder()
589                 .parse(new InputSource(new StringReader(xml)));
590         domConfiguration = document.getDomConfig();
591     }
592
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();
599     }
600
601     private class ErrorRecorder implements DOMErrorHandler {
602         private final List<DOMError> errors = new ArrayList<DOMError>();
603
604         public boolean handleError(DOMError error) {
605             errors.add(error);
606             return true;
607         }
608
609         public void assertAllErrors(int severity, String type) {
610             assertAllErrors("Expected one or more " + type + " errors", severity, type);
611         }
612
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());
618             }
619             errors.clear();
620         }
621     }
622 }