1/*
2 * Copyright (c) 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.parsers;
22
23import com.sun.org.apache.xerces.internal.impl.Constants;
24import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
25import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
26import com.sun.org.apache.xerces.internal.util.SymbolTable;
27import com.sun.org.apache.xerces.internal.utils.ObjectFactory;
28import com.sun.org.apache.xerces.internal.xni.XNIException;
29import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
30import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
31import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarLoader;
32import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
33import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
34import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
35import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
36import java.io.IOException;
37import java.util.Collections;
38import java.util.Enumeration;
39import java.util.HashMap;
40import java.util.Locale;
41import java.util.Map;
42
43/**
44 * <p> This class provides an easy way for a user to preparse grammars
45 * of various types.  By default, it knows how to preparse external
46 * DTD's and schemas; it provides an easy way for user applications to
47 * register classes that know how to parse additional grammar types.
48 * By default, it does no grammar caching; but it provides ways for
49 * user applications to do so.
50 *
51 * @author Neil Graham, IBM
52 *
53 */
54public class XMLGrammarPreparser {
55
56    //
57    // Constants
58    //
59
60    // feature:  continue-after-fatal-error
61    private final static String CONTINUE_AFTER_FATAL_ERROR =
62        Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;
63
64    /** Property identifier: symbol table. */
65    protected static final String SYMBOL_TABLE =
66        Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
67
68    /** Property identifier: error reporter. */
69    protected static final String ERROR_REPORTER =
70        Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY;
71
72    /** Property identifier: error handler. */
73    protected static final String ERROR_HANDLER =
74        Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY;
75
76    /** Property identifier: entity resolver. */
77    protected static final String ENTITY_RESOLVER =
78        Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
79
80    /** Property identifier: grammar pool . */
81    protected static final String GRAMMAR_POOL =
82        Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY;
83
84    // the "built-in" grammar loaders
85    private static final Map<String, String> KNOWN_LOADERS;
86
87    static {
88        Map<String, String> loaders = new HashMap<>();
89        loaders.put(XMLGrammarDescription.XML_SCHEMA,
90            "com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader");
91        loaders.put(XMLGrammarDescription.XML_DTD,
92            "com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDLoader");
93        KNOWN_LOADERS = Collections.unmodifiableMap(loaders);
94    }
95
96    /** Recognized properties. */
97    private static final String[] RECOGNIZED_PROPERTIES = {
98        SYMBOL_TABLE,
99        ERROR_REPORTER,
100        ERROR_HANDLER,
101        ENTITY_RESOLVER,
102        GRAMMAR_POOL,
103    };
104
105    // Data
106    protected SymbolTable fSymbolTable;
107    protected XMLErrorReporter fErrorReporter;
108    protected XMLEntityResolver fEntityResolver;
109    protected XMLGrammarPool fGrammarPool;
110
111    protected Locale fLocale;
112
113    // Map holding our loaders
114    private Map<String, XMLGrammarLoader> fLoaders;
115
116    //
117    // Constructors
118    //
119
120    /** Default constructor. */
121    public XMLGrammarPreparser() {
122        this(new SymbolTable());
123    } // <init>()
124
125    /**
126     * Constructs a preparser using the specified symbol table.
127     *
128     * @param symbolTable The symbol table to use.
129     */
130    public XMLGrammarPreparser (SymbolTable symbolTable) {
131        fSymbolTable = symbolTable;
132
133        fLoaders = new HashMap<>();
134        fErrorReporter = new XMLErrorReporter();
135        setLocale(Locale.getDefault());
136        fEntityResolver = new XMLEntityManager();
137        // those are all the basic properties...
138    } // <init>(SymbolTable)
139
140    //
141    // Public methods
142    //
143
144    /*
145    * Register a type of grammar to make it preparsable.   If
146    * the second parameter is null, the parser will use its  built-in
147    * facilities for that grammar type.
148    * This should be called by the application immediately
149    * after creating this object and before initializing any properties/features.
150    * @param type   URI identifying the type of the grammar
151    * @param loader an object capable of preparsing that type; null if the ppreparser should use built-in knowledge.
152    * @return true if successful; false if no built-in knowledge of
153    *       the type or if unable to instantiate the string we know about
154    */
155    public boolean registerPreparser(String grammarType, XMLGrammarLoader loader) {
156        if(loader == null) { // none specified!
157            if(KNOWN_LOADERS.containsKey(grammarType)) {
158                // got one; just instantiate it...
159                String loaderName = (String)KNOWN_LOADERS.get(grammarType);
160                try {
161                    XMLGrammarLoader gl = (XMLGrammarLoader)(ObjectFactory.newInstance(loaderName, true));
162                    fLoaders.put(grammarType, gl);
163                } catch (Exception e) {
164                    return false;
165                }
166                return true;
167            }
168            return false;
169        }
170        // were given one
171        fLoaders.put(grammarType, loader);
172        return true;
173    } // registerPreparser(String, XMLGrammarLoader):  boolean
174
175    /**
176     * Parse a grammar from a location identified by an
177     * XMLInputSource.
178     * This method also adds this grammar to the XMLGrammarPool
179     *
180     * @param type The type of the grammar to be constructed
181     * @param is The XMLInputSource containing this grammar's
182     * information
183     * <strong>If a URI is included in the systemId field, the parser will not expand this URI or make it
184     * available to the EntityResolver</strong>
185     * @return The newly created <code>Grammar</code>.
186     * @exception XNIException thrown on an error in grammar
187     * construction
188     * @exception IOException thrown if an error is encountered
189     * in reading the file
190     */
191    public Grammar preparseGrammar(String type, XMLInputSource
192                is) throws XNIException, IOException {
193        if(fLoaders.containsKey(type)) {
194            XMLGrammarLoader gl = fLoaders.get(type);
195            // make sure gl's been set up with all the "basic" properties:
196            gl.setProperty(SYMBOL_TABLE, fSymbolTable);
197            gl.setProperty(ENTITY_RESOLVER, fEntityResolver);
198            gl.setProperty(ERROR_REPORTER, fErrorReporter);
199            // potentially, not all will support this one...
200            if(fGrammarPool != null) {
201                try {
202                    gl.setProperty(GRAMMAR_POOL, fGrammarPool);
203                } catch(Exception e) {
204                    // too bad...
205                }
206            }
207            return gl.loadGrammar(is);
208        }
209        return null;
210    } // preparseGrammar(String, XMLInputSource):  Grammar
211
212    /**
213     * Set the locale to use for messages.
214     *
215     * @param locale The locale object to use for localization of messages.
216     *
217     * @exception XNIException Thrown if the parser does not support the
218     *                         specified locale.
219     */
220    public void setLocale(Locale locale) {
221        fLocale = locale;
222        fErrorReporter.setLocale(locale);
223    } // setLocale(Locale)
224
225    /** Return the Locale the XMLGrammarLoader is using. */
226    public Locale getLocale() {
227        return fLocale;
228    } // getLocale():  Locale
229
230
231    /**
232     * Sets the error handler.
233     *
234     * @param errorHandler The error handler.
235     */
236    public void setErrorHandler(XMLErrorHandler errorHandler) {
237        fErrorReporter.setProperty(ERROR_HANDLER, errorHandler);
238    } // setErrorHandler(XMLErrorHandler)
239
240    /** Returns the registered error handler.  */
241    public XMLErrorHandler getErrorHandler() {
242        return fErrorReporter.getErrorHandler();
243    } // getErrorHandler():  XMLErrorHandler
244
245    /**
246     * Sets the entity resolver.
247     *
248     * @param entityResolver The new entity resolver.
249     */
250    public void setEntityResolver(XMLEntityResolver entityResolver) {
251        fEntityResolver = entityResolver;
252    } // setEntityResolver(XMLEntityResolver)
253
254    /** Returns the registered entity resolver.  */
255    public XMLEntityResolver getEntityResolver() {
256        return fEntityResolver;
257    } // getEntityResolver():  XMLEntityResolver
258
259    /**
260     * Sets the grammar pool.
261     *
262     * @param grammarPool The new grammar pool.
263     */
264    public void setGrammarPool(XMLGrammarPool grammarPool) {
265        fGrammarPool = grammarPool;
266    } // setGrammarPool(XMLGrammarPool)
267
268    /** Returns the registered grammar pool.  */
269    public XMLGrammarPool getGrammarPool() {
270        return fGrammarPool;
271    } // getGrammarPool():  XMLGrammarPool
272
273    // it's possible the application may want access to a certain loader to do
274    // some custom work.
275    public XMLGrammarLoader getLoader(String type) {
276        return fLoaders.get(type);
277    } // getLoader(String):  XMLGrammarLoader
278
279    // set a feature.  This method tries to set it on all
280    // registered loaders; it eats any resulting exceptions.  If
281    // an app needs to know if a particular feature is supported
282    // by a grammar loader of a particular type, it will have
283    // to retrieve that loader and use the loader's setFeature method.
284    public void setFeature(String featureId, boolean value) {
285        for (Map.Entry<String, XMLGrammarLoader> entry : fLoaders.entrySet()) {
286            try {
287                XMLGrammarLoader gl = entry.getValue();
288                gl.setFeature(featureId, value);
289            } catch(Exception e) {
290                // eat it up...
291            }
292        }
293        // since our error reporter is a property we set later,
294        // make sure features it understands are also set.
295        if(featureId.equals(CONTINUE_AFTER_FATAL_ERROR)) {
296            fErrorReporter.setFeature(CONTINUE_AFTER_FATAL_ERROR, value);
297        }
298    } //setFeature(String, boolean)
299
300    // set a property.  This method tries to set it on all
301    // registered loaders; it eats any resulting exceptions.  If
302    // an app needs to know if a particular property is supported
303    // by a grammar loader of a particular type, it will have
304    // to retrieve that loader and use the loader's setProperty method.
305    // <p> <strong>An application should use the explicit method
306    // in this class to set "standard" properties like error handler etc.</strong>
307    public void setProperty(String propId, Object value) {
308        for (Map.Entry<String, XMLGrammarLoader> entry : fLoaders.entrySet()) {
309            try {
310                XMLGrammarLoader gl = entry.getValue();
311                gl.setProperty(propId, value);
312            } catch(Exception e) {
313                // eat it up...
314            }
315        }
316    } //setProperty(String, Object)
317
318    // get status of feature in a particular loader.  This
319    // catches no exceptions--including NPE's--so the application had
320    // better make sure the loader exists and knows about this feature.
321    // @param type type of grammar to look for the feature in.
322    // @param featureId the feature string to query.
323    // @return the value of the feature.
324    public boolean getFeature(String type, String featureId) {
325        XMLGrammarLoader gl = fLoaders.get(type);
326        return gl.getFeature(featureId);
327    } // getFeature (String, String):  boolean
328
329    // get status of property in a particular loader.  This
330    // catches no exceptions--including NPE's--so the application had
331    // better make sure the loader exists and knows about this property.
332    // <strong>For standard properties--that will be supported
333    // by all loaders--the specific methods should be queried!</strong>
334    // @param type type of grammar to look for the property in.
335    // @param propertyId the property string to query.
336    // @return the value of the property.
337    public Object getProperty(String type, String propertyId) {
338        XMLGrammarLoader gl = fLoaders.get(type);
339        return gl.getProperty(propertyId);
340    } // getProperty(String, String):  Object
341} // class XMLGrammarPreparser
342