1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package org.apache.xerces.parsers; 19 20 import java.io.IOException; 21 import java.util.Enumeration; 22 import java.util.Hashtable; 23 import java.util.Locale; 24 25 import org.apache.xerces.impl.Constants; 26 import org.apache.xerces.impl.XMLEntityManager; 27 import org.apache.xerces.impl.XMLErrorReporter; 28 import org.apache.xerces.util.SymbolTable; 29 import org.apache.xerces.xni.XNIException; 30 import org.apache.xerces.xni.grammars.Grammar; 31 import org.apache.xerces.xni.grammars.XMLGrammarDescription; 32 import org.apache.xerces.xni.grammars.XMLGrammarLoader; 33 import org.apache.xerces.xni.grammars.XMLGrammarPool; 34 import org.apache.xerces.xni.parser.XMLEntityResolver; 35 import org.apache.xerces.xni.parser.XMLErrorHandler; 36 import org.apache.xerces.xni.parser.XMLInputSource; 37 38 /** 39 * <p> This class provides an easy way for a user to preparse grammars 40 * of various types. By default, it knows how to preparse external 41 * DTD's and schemas; it provides an easy way for user applications to 42 * register classes that know how to parse additional grammar types. 43 * By default, it does no grammar caching; but it provides ways for 44 * user applications to do so. 45 * 46 * @author Neil Graham, IBM 47 * 48 * @version $Id: XMLGrammarPreparser.java 521449 2007-03-22 20:35:42Z mrglavas $ 49 */ 50 public class XMLGrammarPreparser { 51 52 // 53 // Constants 54 // 55 56 // feature: continue-after-fatal-error 57 private final static String CONTINUE_AFTER_FATAL_ERROR = 58 Constants.XERCES_FEATURE_PREFIX + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE; 59 60 /** Property identifier: symbol table. */ 61 protected static final String SYMBOL_TABLE = 62 Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY; 63 64 /** Property identifier: error reporter. */ 65 protected static final String ERROR_REPORTER = 66 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_REPORTER_PROPERTY; 67 68 /** Property identifier: error handler. */ 69 protected static final String ERROR_HANDLER = 70 Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY; 71 72 /** Property identifier: entity resolver. */ 73 protected static final String ENTITY_RESOLVER = 74 Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY; 75 76 /** Property identifier: grammar pool . */ 77 protected static final String GRAMMAR_POOL = 78 Constants.XERCES_PROPERTY_PREFIX + Constants.XMLGRAMMAR_POOL_PROPERTY; 79 80 // the "built-in" grammar loaders 81 private static final Hashtable KNOWN_LOADERS = new Hashtable(); 82 83 static { 84 KNOWN_LOADERS.put(XMLGrammarDescription.XML_SCHEMA, 85 "org.apache.xerces.impl.xs.XMLSchemaLoader"); 86 KNOWN_LOADERS.put(XMLGrammarDescription.XML_DTD, 87 "org.apache.xerces.impl.dtd.XMLDTDLoader"); 88 } 89 90 /** Recognized properties. */ 91 private static final String[] RECOGNIZED_PROPERTIES = { 92 SYMBOL_TABLE, 93 ERROR_REPORTER, 94 ERROR_HANDLER, 95 ENTITY_RESOLVER, 96 GRAMMAR_POOL, 97 }; 98 99 // Data 100 protected SymbolTable fSymbolTable; 101 protected XMLErrorReporter fErrorReporter; 102 protected XMLEntityResolver fEntityResolver; 103 protected XMLGrammarPool fGrammarPool; 104 105 protected Locale fLocale; 106 107 // Hashtable holding our loaders 108 private Hashtable fLoaders; 109 110 // The number of times the configuration has been modified. 111 private int fModCount = 1; 112 113 // 114 // Constructors 115 // 116 117 /** Default constructor. */ 118 public XMLGrammarPreparser() { 119 this(new SymbolTable()); 120 } // <init>() 121 122 /** 123 * Constructs a preparser using the specified symbol table. 124 * 125 * @param symbolTable The symbol table to use. 126 */ 127 public XMLGrammarPreparser (SymbolTable symbolTable) { 128 fSymbolTable = symbolTable; 129 130 fLoaders = new Hashtable(); 131 setLocale(Locale.getDefault()); 132 fErrorReporter = new XMLErrorReporter(); 133 fErrorReporter.setLocale(fLocale); 134 fEntityResolver = new XMLEntityManager(); 135 // those are all the basic properties... 136 } // <init>(SymbolTable) 137 138 // 139 // Public methods 140 // 141 142 /* 143 * Register a type of grammar to make it preparsable. If 144 * the second parameter is null, the parser will use its built-in 145 * facilities for that grammar type. 146 * This should be called by the application immediately 147 * after creating this object and before initializing any properties/features. 148 * @param type URI identifying the type of the grammar 149 * @param loader an object capable of preparsing that type; null if the ppreparser should use built-in knowledge. 150 * @return true if successful; false if no built-in knowledge of 151 * the type or if unable to instantiate the string we know about 152 */ 153 public boolean registerPreparser(String grammarType, XMLGrammarLoader loader) { 154 if(loader == null) { // none specified! 155 if(KNOWN_LOADERS.containsKey(grammarType)) { 156 // got one; just instantiate it... 157 String loaderName = (String)KNOWN_LOADERS.get(grammarType); 158 try { 159 ClassLoader cl = ObjectFactory.findClassLoader(); 160 XMLGrammarLoader gl = (XMLGrammarLoader)(ObjectFactory.newInstance(loaderName, cl, true)); 161 fLoaders.put(grammarType, new XMLGrammarLoaderContainer(gl)); 162 } catch (Exception e) { 163 return false; 164 } 165 return true; 166 } 167 return false; 168 } 169 // were given one 170 fLoaders.put(grammarType, new XMLGrammarLoaderContainer(loader)); 171 return true; 172 } // registerPreparser(String, XMLGrammarLoader): boolean 173 174 /** 175 * Parse a grammar from a location identified by an 176 * XMLInputSource. 177 * This method also adds this grammar to the XMLGrammarPool 178 * 179 * @param type The type of the grammar to be constructed 180 * @param is The XMLInputSource containing this grammar's 181 * information 182 * <strong>If a URI is included in the systemId field, the parser will not expand this URI or make it 183 * available to the EntityResolver</strong> 184 * @return The newly created <code>Grammar</code>. 185 * @exception XNIException thrown on an error in grammar 186 * construction 187 * @exception IOException thrown if an error is encountered 188 * in reading the file 189 */ 190 public Grammar preparseGrammar(String type, XMLInputSource 191 is) throws XNIException, IOException { 192 if (fLoaders.containsKey(type)) { 193 XMLGrammarLoaderContainer xglc = (XMLGrammarLoaderContainer) fLoaders.get(type); 194 XMLGrammarLoader gl = xglc.loader; 195 if (xglc.modCount != fModCount) { 196 // make sure gl's been set up with all the "basic" properties: 197 gl.setProperty(SYMBOL_TABLE, fSymbolTable); 198 gl.setProperty(ENTITY_RESOLVER, fEntityResolver); 199 gl.setProperty(ERROR_REPORTER, fErrorReporter); 200 // potentially, not all will support this one... 201 if (fGrammarPool != null) { 202 try { 203 gl.setProperty(GRAMMAR_POOL, fGrammarPool); 204 } catch(Exception e) { 205 // too bad... 206 } 207 } 208 xglc.modCount = fModCount; 209 } 210 return gl.loadGrammar(is); 211 } 212 return null; 213 } // preparseGrammar(String, XMLInputSource): Grammar 214 215 /** 216 * Set the locale to use for messages. 217 * 218 * @param locale The locale object to use for localization of messages. 219 * 220 * @exception XNIException Thrown if the parser does not support the 221 * specified locale. 222 */ 223 public void setLocale(Locale locale) { 224 fLocale = locale; 225 } // setLocale(Locale) 226 227 /** Return the Locale the XMLGrammarLoader is using. */ 228 public Locale getLocale() { 229 return fLocale; 230 } // getLocale(): Locale 231 232 233 /** 234 * Sets the error handler. 235 * 236 * @param errorHandler The error handler. 237 */ 238 public void setErrorHandler(XMLErrorHandler errorHandler) { 239 fErrorReporter.setProperty(ERROR_HANDLER, errorHandler); 240 } // setErrorHandler(XMLErrorHandler) 241 242 /** Returns the registered error handler. */ 243 public XMLErrorHandler getErrorHandler() { 244 return fErrorReporter.getErrorHandler(); 245 } // getErrorHandler(): XMLErrorHandler 246 247 /** 248 * Sets the entity resolver. 249 * 250 * @param entityResolver The new entity resolver. 251 */ 252 public void setEntityResolver(XMLEntityResolver entityResolver) { 253 if (fEntityResolver != entityResolver) { 254 // Overflow. We actually need to reset the 255 // modCount on every XMLGrammarLoaderContainer. 256 if (++fModCount < 0) { 257 clearModCounts(); 258 } 259 fEntityResolver = entityResolver; 260 } 261 } // setEntityResolver(XMLEntityResolver) 262 263 /** Returns the registered entity resolver. */ 264 public XMLEntityResolver getEntityResolver() { 265 return fEntityResolver; 266 } // getEntityResolver(): XMLEntityResolver 267 268 /** 269 * Sets the grammar pool. 270 * 271 * @param grammarPool The new grammar pool. 272 */ 273 public void setGrammarPool(XMLGrammarPool grammarPool) { 274 if (fGrammarPool != grammarPool) { 275 // Overflow. We actually need to reset the 276 // modCount on every XMLGrammarLoaderContainer. 277 if (++fModCount < 0) { 278 clearModCounts(); 279 } 280 fGrammarPool = grammarPool; 281 } 282 } // setGrammarPool(XMLGrammarPool) 283 284 /** Returns the registered grammar pool. */ 285 public XMLGrammarPool getGrammarPool() { 286 return fGrammarPool; 287 } // getGrammarPool(): XMLGrammarPool 288 289 // it's possible the application may want access to a certain loader to do 290 // some custom work. 291 public XMLGrammarLoader getLoader(String type) { 292 XMLGrammarLoaderContainer xglc = (XMLGrammarLoaderContainer) fLoaders.get(type); 293 return (xglc != null) ? xglc.loader : null; 294 } // getLoader(String): XMLGrammarLoader 295 296 // set a feature. This method tries to set it on all 297 // registered loaders; it eats any resulting exceptions. If 298 // an app needs to know if a particular feature is supported 299 // by a grammar loader of a particular type, it will have 300 // to retrieve that loader and use the loader's setFeature method. 301 public void setFeature(String featureId, boolean value) { 302 Enumeration loaders = fLoaders.elements(); 303 while (loaders.hasMoreElements()) { 304 XMLGrammarLoader gl = ((XMLGrammarLoaderContainer)loaders.nextElement()).loader; 305 try { 306 gl.setFeature(featureId, value); 307 } catch(Exception e) { 308 // eat it up... 309 } 310 } 311 // since our error reporter is a property we set later, 312 // make sure features it understands are also set. 313 if(featureId.equals(CONTINUE_AFTER_FATAL_ERROR)) { 314 fErrorReporter.setFeature(CONTINUE_AFTER_FATAL_ERROR, value); 315 } 316 } //setFeature(String, boolean) 317 318 // set a property. This method tries to set it on all 319 // registered loaders; it eats any resulting exceptions. If 320 // an app needs to know if a particular property is supported 321 // by a grammar loader of a particular type, it will have 322 // to retrieve that loader and use the loader's setProperty method. 323 // <p> <strong>An application should use the explicit method 324 // in this class to set "standard" properties like error handler etc.</strong> 325 public void setProperty(String propId, Object value) { 326 Enumeration loaders = fLoaders.elements(); 327 while (loaders.hasMoreElements()) { 328 XMLGrammarLoader gl = ((XMLGrammarLoaderContainer)loaders.nextElement()).loader; 329 try { 330 gl.setProperty(propId, value); 331 } catch(Exception e) { 332 // eat it up... 333 } 334 } 335 } //setProperty(String, Object) 336 337 // get status of feature in a particular loader. This 338 // catches no exceptions--including NPE's--so the application had 339 // better make sure the loader exists and knows about this feature. 340 // @param type type of grammar to look for the feature in. 341 // @param featureId the feature string to query. 342 // @return the value of the feature. 343 public boolean getFeature(String type, String featureId) { 344 XMLGrammarLoader gl = ((XMLGrammarLoaderContainer)fLoaders.get(type)).loader; 345 return gl.getFeature(featureId); 346 } // getFeature (String, String): boolean 347 348 // get status of property in a particular loader. This 349 // catches no exceptions--including NPE's--so the application had 350 // better make sure the loader exists and knows about this property. 351 // <strong>For standard properties--that will be supported 352 // by all loaders--the specific methods should be queried!</strong> 353 // @param type type of grammar to look for the property in. 354 // @param propertyId the property string to query. 355 // @return the value of the property. 356 public Object getProperty(String type, String propertyId) { 357 XMLGrammarLoader gl = ((XMLGrammarLoaderContainer)fLoaders.get(type)).loader; 358 return gl.getProperty(propertyId); 359 } // getProperty(String, String): Object 360 361 /** 362 * Container for an XMLGrammarLoader. Also holds the modCount 363 * that the XMLGrammarPreparser had the last time it was used. 364 */ 365 static class XMLGrammarLoaderContainer { 366 public final XMLGrammarLoader loader; 367 public int modCount = 0; 368 public XMLGrammarLoaderContainer(XMLGrammarLoader loader) { 369 this.loader = loader; 370 } 371 } 372 373 private void clearModCounts() { 374 Enumeration loaders = fLoaders.elements(); 375 while (loaders.hasMoreElements()) { 376 XMLGrammarLoaderContainer xglc = (XMLGrammarLoaderContainer) loaders.nextElement(); 377 xglc.modCount = 0; 378 } 379 fModCount = 1; 380 } 381 382 } // class XMLGrammarPreparser