1/*
2 * Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
3 */
4/*
5 * Licensed to the Apache Software Foundation (ASF) under one or more
6 * contributor license agreements.  See the NOTICE file distributed with
7 * this work for additional information regarding copyright ownership.
8 * The ASF licenses this file to You under the Apache License, Version 2.0
9 * (the "License"); you may not use this file except in compliance with
10 * the License.  You may obtain a copy of the License at
11 *
12 *      http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 */
20
21package com.sun.org.apache.xerces.internal.jaxp;
22
23import java.io.IOException;
24import java.util.Iterator;
25import java.util.Map;
26
27import javax.xml.parsers.DocumentBuilder;
28import javax.xml.validation.Schema;
29import javax.xml.XMLConstants;
30
31import com.sun.org.apache.xerces.internal.dom.DOMImplementationImpl;
32import com.sun.org.apache.xerces.internal.dom.DOMMessageFormatter;
33import com.sun.org.apache.xerces.internal.impl.Constants;
34import com.sun.org.apache.xerces.internal.impl.validation.ValidationManager;
35import com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaValidator;
36import com.sun.org.apache.xerces.internal.jaxp.validation.XSGrammarPoolContainer;
37import com.sun.org.apache.xerces.internal.parsers.DOMParser;
38import com.sun.org.apache.xerces.internal.utils.XMLSecurityManager;
39import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager;
40import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager.Property;
41import com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager.State;
42import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
43import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
44import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
45import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
46import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
47import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
48import org.w3c.dom.DOMImplementation;
49import org.w3c.dom.Document;
50import org.xml.sax.EntityResolver;
51import org.xml.sax.ErrorHandler;
52import org.xml.sax.InputSource;
53import org.xml.sax.SAXException;
54import org.xml.sax.SAXNotRecognizedException;
55import org.xml.sax.SAXNotSupportedException;
56
57/**
58 * @author Rajiv Mordani
59 * @author Edwin Goei
60 */
61public class DocumentBuilderImpl extends DocumentBuilder
62        implements JAXPConstants
63{
64    /** Feature identifier: namespaces. */
65    private static final String NAMESPACES_FEATURE =
66        Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
67
68    /** Feature identifier: include ignorable white space. */
69    private static final String INCLUDE_IGNORABLE_WHITESPACE =
70        Constants.XERCES_FEATURE_PREFIX + Constants.INCLUDE_IGNORABLE_WHITESPACE;
71
72    /** Feature identifier: create entiry ref nodes feature. */
73    private static final String CREATE_ENTITY_REF_NODES_FEATURE =
74        Constants.XERCES_FEATURE_PREFIX + Constants.CREATE_ENTITY_REF_NODES_FEATURE;
75
76    /** Feature identifier: include comments feature. */
77    private static final String INCLUDE_COMMENTS_FEATURE =
78        Constants.XERCES_FEATURE_PREFIX + Constants.INCLUDE_COMMENTS_FEATURE;
79
80    /** Feature identifier: create cdata nodes feature. */
81    private static final String CREATE_CDATA_NODES_FEATURE =
82        Constants.XERCES_FEATURE_PREFIX + Constants.CREATE_CDATA_NODES_FEATURE;
83
84    /** Feature identifier: XInclude processing */
85    private static final String XINCLUDE_FEATURE =
86        Constants.XERCES_FEATURE_PREFIX + Constants.XINCLUDE_FEATURE;
87
88    /** feature identifier: XML Schema validation */
89    private static final String XMLSCHEMA_VALIDATION_FEATURE =
90        Constants.XERCES_FEATURE_PREFIX + Constants.SCHEMA_VALIDATION_FEATURE;
91
92    /** Feature identifier: validation */
93    private static final String VALIDATION_FEATURE =
94        Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
95
96    /** Property identifier: security manager. */
97    private static final String SECURITY_MANAGER =
98        Constants.XERCES_PROPERTY_PREFIX + Constants.SECURITY_MANAGER_PROPERTY;
99
100    /** Property identifier: Security property manager. */
101    private static final String XML_SECURITY_PROPERTY_MANAGER =
102            Constants.XML_SECURITY_PROPERTY_MANAGER;
103
104    /** property identifier: access external dtd. */
105    public static final String ACCESS_EXTERNAL_DTD = XMLConstants.ACCESS_EXTERNAL_DTD;
106
107    /** Property identifier: access to external schema */
108    public static final String ACCESS_EXTERNAL_SCHEMA = XMLConstants.ACCESS_EXTERNAL_SCHEMA;
109
110
111    private final DOMParser domParser;
112    private final Schema grammar;
113
114    private final XMLComponent fSchemaValidator;
115    private final XMLComponentManager fSchemaValidatorComponentManager;
116    private final ValidationManager fSchemaValidationManager;
117    private final UnparsedEntityHandler fUnparsedEntityHandler;
118
119    /** Initial ErrorHandler */
120    private final ErrorHandler fInitErrorHandler;
121
122    /** Initial EntityResolver */
123    private final EntityResolver fInitEntityResolver;
124
125    private XMLSecurityManager fSecurityManager;
126    private XMLSecurityPropertyManager fSecurityPropertyMgr;
127
128    DocumentBuilderImpl(DocumentBuilderFactoryImpl dbf, Map<String, Object> dbfAttrs,
129            Map<String, Boolean> features)
130        throws SAXNotRecognizedException, SAXNotSupportedException {
131        this(dbf, dbfAttrs, features, false);
132    }
133
134    DocumentBuilderImpl(DocumentBuilderFactoryImpl dbf, Map<String, Object> dbfAttrs,
135            Map<String, Boolean> features, boolean secureProcessing)
136        throws SAXNotRecognizedException, SAXNotSupportedException
137    {
138        domParser = new DOMParser();
139
140        // If validating, provide a default ErrorHandler that prints
141        // validation errors with a warning telling the user to set an
142        // ErrorHandler
143        if (dbf.isValidating()) {
144            fInitErrorHandler = new DefaultValidationErrorHandler(domParser.getXMLParserConfiguration().getLocale());
145            setErrorHandler(fInitErrorHandler);
146        }
147        else {
148            fInitErrorHandler = domParser.getErrorHandler();
149        }
150
151        domParser.setFeature(VALIDATION_FEATURE, dbf.isValidating());
152
153        // "namespaceAware" == SAX Namespaces feature
154        domParser.setFeature(NAMESPACES_FEATURE, dbf.isNamespaceAware());
155
156        // Set various parameters obtained from DocumentBuilderFactory
157        domParser.setFeature(INCLUDE_IGNORABLE_WHITESPACE,
158                !dbf.isIgnoringElementContentWhitespace());
159        domParser.setFeature(CREATE_ENTITY_REF_NODES_FEATURE,
160                !dbf.isExpandEntityReferences());
161        domParser.setFeature(INCLUDE_COMMENTS_FEATURE,
162                !dbf.isIgnoringComments());
163        domParser.setFeature(CREATE_CDATA_NODES_FEATURE,
164                !dbf.isCoalescing());
165
166        // Avoid setting the XInclude processing feature if the value is false.
167        // This will keep the configuration from throwing an exception if it
168        // does not support XInclude.
169        if (dbf.isXIncludeAware()) {
170            domParser.setFeature(XINCLUDE_FEATURE, true);
171        }
172
173        fSecurityPropertyMgr = new XMLSecurityPropertyManager();
174        domParser.setProperty(XML_SECURITY_PROPERTY_MANAGER, fSecurityPropertyMgr);
175
176        fSecurityManager = new XMLSecurityManager(secureProcessing);
177        domParser.setProperty(SECURITY_MANAGER, fSecurityManager);
178
179        if (secureProcessing) {
180            /**
181             * If secure processing is explicitly set on the factory, the
182             * access properties will be set unless the corresponding
183             * System Properties or jaxp.properties are set
184             */
185            if (features != null) {
186                Boolean temp = features.get(XMLConstants.FEATURE_SECURE_PROCESSING);
187                if (temp != null && temp) {
188                    fSecurityPropertyMgr.setValue(Property.ACCESS_EXTERNAL_DTD,
189                            State.FSP, Constants.EXTERNAL_ACCESS_DEFAULT_FSP);
190                    fSecurityPropertyMgr.setValue(Property.ACCESS_EXTERNAL_SCHEMA,
191                            State.FSP, Constants.EXTERNAL_ACCESS_DEFAULT_FSP);
192                }
193            }
194        }
195
196        this.grammar = dbf.getSchema();
197        if (grammar != null) {
198            XMLParserConfiguration config = domParser.getXMLParserConfiguration();
199            XMLComponent validatorComponent = null;
200            /** For Xerces grammars, use built-in schema validator. **/
201            if (grammar instanceof XSGrammarPoolContainer) {
202                validatorComponent = new XMLSchemaValidator();
203                fSchemaValidationManager = new ValidationManager();
204                fUnparsedEntityHandler = new UnparsedEntityHandler(fSchemaValidationManager);
205                config.setDTDHandler(fUnparsedEntityHandler);
206                fUnparsedEntityHandler.setDTDHandler(domParser);
207                domParser.setDTDSource(fUnparsedEntityHandler);
208                fSchemaValidatorComponentManager = new SchemaValidatorConfiguration(config,
209                        (XSGrammarPoolContainer) grammar, fSchemaValidationManager);
210            }
211            /** For third party grammars, use the JAXP validator component. **/
212            else {
213                validatorComponent = new JAXPValidatorComponent(grammar.newValidatorHandler());
214                fSchemaValidationManager = null;
215                fUnparsedEntityHandler = null;
216                fSchemaValidatorComponentManager = config;
217            }
218            config.addRecognizedFeatures(validatorComponent.getRecognizedFeatures());
219            config.addRecognizedProperties(validatorComponent.getRecognizedProperties());
220            setFeatures(features);      // Must set before calling setDocumentHandler()
221            config.setDocumentHandler((XMLDocumentHandler) validatorComponent);
222            ((XMLDocumentSource)validatorComponent).setDocumentHandler(domParser);
223            domParser.setDocumentSource((XMLDocumentSource) validatorComponent);
224            fSchemaValidator = validatorComponent;
225        }
226        else {
227            fSchemaValidationManager = null;
228            fUnparsedEntityHandler = null;
229            fSchemaValidatorComponentManager = null;
230            fSchemaValidator = null;
231            setFeatures(features);
232        }
233
234        //setAttribute override those that may be set by other means
235        setDocumentBuilderFactoryAttributes(dbfAttrs);
236
237        // Initial EntityResolver
238        fInitEntityResolver = domParser.getEntityResolver();
239    }
240
241    private void setFeatures( Map<String, Boolean> features)
242        throws SAXNotSupportedException, SAXNotRecognizedException {
243        if (features != null) {
244            for (Map.Entry<String, Boolean> entry : features.entrySet()) {
245                domParser.setFeature(entry.getKey(), entry.getValue());
246        }
247    }
248    }
249
250    /**
251     * Set any DocumentBuilderFactory attributes of our underlying DOMParser
252     *
253     * Note: code does not handle possible conflicts between DOMParser
254     * attribute names and JAXP specific attribute names,
255     * eg. DocumentBuilderFactory.setValidating()
256     */
257    private void setDocumentBuilderFactoryAttributes( Map<String, Object> dbfAttrs)
258        throws SAXNotSupportedException, SAXNotRecognizedException
259    {
260        if (dbfAttrs == null) {
261            // Nothing to do
262            return;
263        }
264
265        for (Map.Entry<String, Object> entry : dbfAttrs.entrySet()) {
266            String name = entry.getKey();
267            Object val = entry.getValue();
268            if (val instanceof Boolean) {
269                // Assume feature
270                domParser.setFeature(name, (Boolean)val);
271            } else {
272                // Assume property
273                if (JAXP_SCHEMA_LANGUAGE.equals(name)) {
274                    // JAXP 1.2 support
275                    //None of the properties will take effect till the setValidating(true) has been called
276                    if ( W3C_XML_SCHEMA.equals(val) ) {
277                        if( isValidating() ) {
278                            domParser.setFeature(XMLSCHEMA_VALIDATION_FEATURE, true);
279                            // this should allow us not to emit DTD errors, as expected by the
280                            // spec when schema validation is enabled
281                            domParser.setProperty(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
282                        }
283                     }
284                 } else if(JAXP_SCHEMA_SOURCE.equals(name)){
285                    if( isValidating() ) {
286                        String value=(String)dbfAttrs.get(JAXP_SCHEMA_LANGUAGE);
287                        if(value !=null && W3C_XML_SCHEMA.equals(value)){
288                            domParser.setProperty(name, val);
289                        }else{
290                            throw new IllegalArgumentException(
291                                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
292                                "jaxp-order-not-supported",
293                                new Object[] {JAXP_SCHEMA_LANGUAGE, JAXP_SCHEMA_SOURCE}));
294                        }
295                     }
296                  } else {
297                     //check if the property is managed by security manager
298                     if (fSecurityManager == null ||
299                             !fSecurityManager.setLimit(name, XMLSecurityManager.State.APIPROPERTY, val)) {
300                         //check if the property is managed by security property manager
301                         if (fSecurityPropertyMgr == null ||
302                                 !fSecurityPropertyMgr.setValue(name, XMLSecurityPropertyManager.State.APIPROPERTY, val)) {
303                             //fall back to the existing property manager
304                             domParser.setProperty(name, val);
305                         }
306                     }
307
308                  }
309             }
310        }
311    }
312
313    /**
314     * Non-preferred: use the getDOMImplementation() method instead of this
315     * one to get a DOM Level 2 DOMImplementation object and then use DOM
316     * Level 2 methods to create a DOM Document object.
317     */
318    public Document newDocument() {
319        return new com.sun.org.apache.xerces.internal.dom.DocumentImpl();
320    }
321
322    public DOMImplementation getDOMImplementation() {
323        return DOMImplementationImpl.getDOMImplementation();
324    }
325
326    public Document parse(InputSource is) throws SAXException, IOException {
327        if (is == null) {
328            throw new IllegalArgumentException(
329                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
330                "jaxp-null-input-source", null));
331        }
332        if (fSchemaValidator != null) {
333            if (fSchemaValidationManager != null) {
334                fSchemaValidationManager.reset();
335                fUnparsedEntityHandler.reset();
336            }
337            resetSchemaValidator();
338        }
339        domParser.parse(is);
340        Document doc = domParser.getDocument();
341        domParser.dropDocumentReferences();
342        return doc;
343    }
344
345    public boolean isNamespaceAware() {
346        try {
347            return domParser.getFeature(NAMESPACES_FEATURE);
348        }
349        catch (SAXException x) {
350            throw new IllegalStateException(x.getMessage());
351        }
352    }
353
354    public boolean isValidating() {
355        try {
356            return domParser.getFeature(VALIDATION_FEATURE);
357        }
358        catch (SAXException x) {
359            throw new IllegalStateException(x.getMessage());
360        }
361    }
362
363    /**
364     * Gets the XInclude processing mode for this parser
365     * @return the state of XInclude processing mode
366     */
367    public boolean isXIncludeAware() {
368        try {
369            return domParser.getFeature(XINCLUDE_FEATURE);
370        }
371        catch (SAXException exc) {
372            return false;
373        }
374    }
375
376    public void setEntityResolver(EntityResolver er) {
377        domParser.setEntityResolver(er);
378    }
379
380    public void setErrorHandler(ErrorHandler eh) {
381        domParser.setErrorHandler(eh);
382    }
383
384    public Schema getSchema() {
385        return grammar;
386    }
387
388    public void reset() {
389        /** Restore the initial error handler. **/
390        if (domParser.getErrorHandler() != fInitErrorHandler) {
391            domParser.setErrorHandler(fInitErrorHandler);
392        }
393        /** Restore the initial entity resolver. **/
394        if (domParser.getEntityResolver() != fInitEntityResolver) {
395            domParser.setEntityResolver(fInitEntityResolver);
396        }
397    }
398
399    // package private
400    DOMParser getDOMParser() {
401        return domParser;
402    }
403
404    private void resetSchemaValidator() throws SAXException {
405        try {
406            fSchemaValidator.reset(fSchemaValidatorComponentManager);
407        }
408        // This should never be thrown from the schema validator.
409        catch (XMLConfigurationException e) {
410            throw new SAXException(e);
411        }
412    }
413}
414