1 /*
   2  * Copyright (c) 2015, 2017, 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 
  21 package com.sun.org.apache.xerces.internal.parsers;
  22 
  23 import com.sun.org.apache.xerces.internal.impl.Constants;
  24 import com.sun.org.apache.xerces.internal.impl.XMLEntityManager;
  25 import com.sun.org.apache.xerces.internal.impl.XMLErrorReporter;
  26 import com.sun.org.apache.xerces.internal.util.SymbolTable;
  27 import com.sun.org.apache.xerces.internal.utils.ObjectFactory;
  28 import com.sun.org.apache.xerces.internal.xni.XNIException;
  29 import com.sun.org.apache.xerces.internal.xni.grammars.Grammar;
  30 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarDescription;
  31 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarLoader;
  32 import com.sun.org.apache.xerces.internal.xni.grammars.XMLGrammarPool;
  33 import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
  34 import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
  35 import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
  36 import java.io.IOException;
  37 import java.util.Collections;
  38 import java.util.HashMap;
  39 import java.util.Locale;
  40 import java.util.Map;
  41 
  42 /**
  43  * <p> This class provides an easy way for a user to preparse grammars
  44  * of various types.  By default, it knows how to preparse external
  45  * DTD's and schemas; it provides an easy way for user applications to
  46  * register classes that know how to parse additional grammar types.
  47  * By default, it does no grammar caching; but it provides ways for
  48  * user applications to do so.
  49  *
  50  * @author Neil Graham, IBM
  51  *
  52  * @LastModified: Oct 2017
  53  */
  54 public 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 = 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