DOMConfigurationImpl.java revision 21d27c095fee51fd6eac6a68d50b79df4dc97d85
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
17package org.apache.harmony.xml.dom;
18
19import org.w3c.dom.DOMConfiguration;
20import org.w3c.dom.DOMErrorHandler;
21import org.w3c.dom.DOMException;
22import org.w3c.dom.DOMStringList;
23
24import java.util.Map;
25import java.util.TreeMap;
26
27/**
28 * A minimal implementation of DOMConfiguration. This implementation uses inner
29 * parameter instances to centralize each parameter's behaviour.
30 */
31public final class DOMConfigurationImpl implements DOMConfiguration {
32
33    private static final Map<String, Parameter> PARAMETERS
34            = new TreeMap<String, Parameter>(String.CASE_INSENSITIVE_ORDER);
35
36    static {
37        /*
38         * True to canonicalize the document (unsupported). This includes
39         * removing DocumentType nodes from the tree and removing unused
40         * namespace declarations. Setting this to true also sets these
41         * parameters:
42         *   entities = false
43         *   normalize-characters = false
44         *   cdata-sections = false
45         *   namespaces = true
46         *   namespace-declarations = true
47         *   well-formed = true
48         *   element-content-whitespace = true
49         * Setting these parameters to another value shall revert the canonical
50         * form to false.
51         */
52        PARAMETERS.put("canonical-form", new FixedParameter(false));
53
54        /*
55         * True to keep existing CDATA nodes; false to replace them/merge them
56         * into adjacent text nodes.
57         */
58        PARAMETERS.put("cdata-sections", new BooleanParameter() {
59            public Object get(DOMConfigurationImpl config) {
60                return config.cdataSections;
61            }
62            public void set(DOMConfigurationImpl config, Object value) {
63                config.cdataSections = (Boolean) value;
64            }
65        });
66
67        /*
68         * True to check character normalization (unsupported).
69         */
70        PARAMETERS.put("check-character-normalization", new FixedParameter(false));
71
72        /*
73         * True to keep comments in the document; false to discard them.
74         */
75        PARAMETERS.put("comments", new BooleanParameter() {
76            public Object get(DOMConfigurationImpl config) {
77                return config.comments;
78            }
79            public void set(DOMConfigurationImpl config, Object value) {
80                config.comments = (Boolean) value;
81            }
82        });
83
84        /*
85         * True to expose schema normalized values. Setting this to true sets
86         * the validate parameter to true. Has no effect when validate is false.
87         */
88        PARAMETERS.put("datatype-normalization", new BooleanParameter() {
89            public Object get(DOMConfigurationImpl config) {
90                return config.datatypeNormalization;
91            }
92            public void set(DOMConfigurationImpl config, Object value) {
93                if ((Boolean) value) {
94                    config.datatypeNormalization = true;
95                    config.validate = true;
96                } else {
97                    config.datatypeNormalization = false;
98                }
99            }
100        });
101
102        /*
103         * True to keep whitespace elements in the document; false to discard
104         * them (unsupported).
105         */
106        PARAMETERS.put("element-content-whitespace", new FixedParameter(true));
107
108        /*
109         * True to keep entity references in the document; false to expand them.
110         */
111        PARAMETERS.put("entities", new BooleanParameter() {
112            public Object get(DOMConfigurationImpl config) {
113                return config.entities;
114            }
115            public void set(DOMConfigurationImpl config, Object value) {
116                config.entities = (Boolean) value;
117            }
118        });
119
120        /*
121         * Handler to be invoked when errors are encountered.
122         */
123        PARAMETERS.put("error-handler", new Parameter() {
124            public Object get(DOMConfigurationImpl config) {
125                return config.errorHandler;
126            }
127            public void set(DOMConfigurationImpl config, Object value) {
128                config.errorHandler = (DOMErrorHandler) value;
129            }
130            public boolean canSet(DOMConfigurationImpl config, Object value) {
131                return value == null || value instanceof DOMErrorHandler;
132            }
133        });
134
135        /*
136         * Bulk alias to set the following parameter values:
137         *   validate-if-schema = false
138         *   entities = false
139         *   datatype-normalization = false
140         *   cdata-sections = false
141         *   namespace-declarations = true
142         *   well-formed = true
143         *   element-content-whitespace = true
144         *   comments = true
145         *   namespaces = true.
146         * Querying this returns true if all of the above parameters have the
147         * listed values; false otherwise.
148         */
149        PARAMETERS.put("infoset", new BooleanParameter() {
150            public Object get(DOMConfigurationImpl config) {
151                // validate-if-schema is always false
152                // element-content-whitespace is always true
153                // namespace-declarations is always true
154                return !config.entities
155                        && !config.datatypeNormalization
156                        && !config.cdataSections
157                        && config.wellFormed
158                        && config.comments
159                        && config.namespaces;
160            }
161            public void set(DOMConfigurationImpl config, Object value) {
162                if ((Boolean) value) {
163                    // validate-if-schema is always false
164                    // element-content-whitespace is always true
165                    // namespace-declarations is always true
166                    config.entities = false;
167                    config.datatypeNormalization = false;
168                    config.cdataSections = false;
169                    config.wellFormed = true;
170                    config.comments = true;
171                    config.namespaces = true;
172                }
173            }
174        });
175
176        /*
177         * True to perform namespace processing; false for none.
178         */
179        PARAMETERS.put("namespaces", new BooleanParameter() {
180            public Object get(DOMConfigurationImpl config) {
181                return config.namespaces;
182            }
183            public void set(DOMConfigurationImpl config, Object value) {
184                config.namespaces = (Boolean) value;
185            }
186        });
187
188        /**
189         * True to include namespace declarations; false to discard them
190         * (unsupported). Even when namespace declarations are discarded,
191         * prefixes are retained.
192         *
193         * Has no effect if namespaces is false.
194         */
195        PARAMETERS.put("namespace-declarations", new FixedParameter(true));
196
197        /*
198         * True to fully normalize characters (unsupported).
199         */
200        PARAMETERS.put("normalize-characters", new FixedParameter(false));
201
202        /*
203         * A list of whitespace-separated URIs representing the schemas to validate
204         * against. Has no effect if schema-type is null.
205         */
206        PARAMETERS.put("schema-location", new Parameter() {
207            public Object get(DOMConfigurationImpl config) {
208                return config.schemaLocation;
209            }
210            public void set(DOMConfigurationImpl config, Object value) {
211                config.schemaLocation = (String) value;
212            }
213            public boolean canSet(DOMConfigurationImpl config, Object value) {
214                return value == null || value instanceof String;
215            }
216        });
217
218        /*
219         * URI representing the type of schema language, such as
220         * "http://www.w3.org/2001/XMLSchema" or "http://www.w3.org/TR/REC-xml".
221         */
222        PARAMETERS.put("schema-type", new Parameter() {
223            public Object get(DOMConfigurationImpl config) {
224                return config.schemaType;
225            }
226            public void set(DOMConfigurationImpl config, Object value) {
227                config.schemaType = (String) value;
228            }
229            public boolean canSet(DOMConfigurationImpl config, Object value) {
230                return value == null || value instanceof String;
231            }
232        });
233
234        /*
235         * True to split CDATA sections containing "]]>"; false to signal an
236         * error instead.
237         */
238        PARAMETERS.put("split-cdata-sections", new BooleanParameter() {
239            public Object get(DOMConfigurationImpl config) {
240                return config.splitCdataSections;
241            }
242            public void set(DOMConfigurationImpl config, Object value) {
243                config.splitCdataSections = (Boolean) value;
244            }
245        });
246
247        /*
248         * True to require validation against a schema or DTD. Validation will
249         * recompute element content whitespace, ID and schema type data.
250         *
251         * Setting this unsets validate-if-schema.
252         */
253        PARAMETERS.put("validate", new BooleanParameter() {
254            public Object get(DOMConfigurationImpl config) {
255                return config.validate;
256            }
257            public void set(DOMConfigurationImpl config, Object value) {
258                // validate-if-schema is always false
259                config.validate = (Boolean) value;
260            }
261        });
262
263        /*
264         * True to validate if a schema was declared (unsupported). Setting this
265         * unsets validate.
266         */
267        PARAMETERS.put("validate-if-schema", new FixedParameter(false));
268
269        /*
270         * True to report invalid characters in node names, attributes, elements,
271         * comments, text, CDATA sections and processing instructions.
272         */
273        PARAMETERS.put("well-formed", new BooleanParameter() {
274            public Object get(DOMConfigurationImpl config) {
275                return config.wellFormed;
276            }
277            public void set(DOMConfigurationImpl config, Object value) {
278                config.wellFormed = (Boolean) value;
279            }
280        });
281
282        // TODO add "resource-resolver" property for use with LS feature...
283    }
284
285    private boolean cdataSections = true;
286    private boolean comments = true;
287    private boolean datatypeNormalization = false;
288    private boolean entities = true;
289    private DOMErrorHandler errorHandler;
290    private boolean namespaces = true;
291    private String schemaLocation;
292    private String schemaType;
293    private boolean splitCdataSections = true;
294    private boolean validate = false;
295    private boolean wellFormed = true;
296
297    interface Parameter {
298        Object get(DOMConfigurationImpl config);
299        void set(DOMConfigurationImpl config, Object value);
300        boolean canSet(DOMConfigurationImpl config, Object value);
301    }
302
303    static class FixedParameter implements Parameter {
304        final Object onlyValue;
305        FixedParameter(Object onlyValue) {
306            this.onlyValue = onlyValue;
307        }
308        public Object get(DOMConfigurationImpl config) {
309            return onlyValue;
310        }
311        public void set(DOMConfigurationImpl config, Object value) {
312            if (!onlyValue.equals(value)) {
313                throw new DOMException(DOMException.NOT_SUPPORTED_ERR,
314                        "Unsupported value: " + value);
315            }
316        }
317        public boolean canSet(DOMConfigurationImpl config, Object value) {
318            return onlyValue.equals(value);
319        }
320    }
321
322    static abstract class BooleanParameter implements Parameter {
323        public boolean canSet(DOMConfigurationImpl config, Object value) {
324            return value instanceof Boolean;
325        }
326    }
327
328    public boolean canSetParameter(String name, Object value) {
329        Parameter parameter = PARAMETERS.get(name);
330        return parameter != null && parameter.canSet(this, value);
331    }
332
333    public void setParameter(String name, Object value) throws DOMException {
334        Parameter parameter = PARAMETERS.get(name);
335        if (parameter == null) {
336            throw new DOMException(DOMException.NOT_FOUND_ERR, "No such parameter: " + name);
337        }
338        try {
339            parameter.set(this, value);
340        } catch (NullPointerException e) {
341            throw new DOMException(DOMException.TYPE_MISMATCH_ERR,
342                    "Null not allowed for " + name);
343        } catch (ClassCastException e) {
344            throw new DOMException(DOMException.TYPE_MISMATCH_ERR,
345                    "Invalid type for " + name + ": " + value.getClass());
346        }
347    }
348
349    public Object getParameter(String name) throws DOMException {
350        Parameter parameter = PARAMETERS.get(name);
351        if (parameter == null) {
352            throw new DOMException(DOMException.NOT_FOUND_ERR, "No such parameter: " + name);
353        }
354        return parameter.get(this);
355    }
356
357    public DOMStringList getParameterNames() {
358        final String[] result = PARAMETERS.keySet().toArray(new String[PARAMETERS.size()]);
359        return new DOMStringList() {
360            public String item(int index) {
361                return index < result.length ? result[index] : null;
362            }
363            public int getLength() {
364                return result.length;
365            }
366            public boolean contains(String str) {
367                return PARAMETERS.containsKey(str); // case-insensitive.
368            }
369        };
370    }
371}
372