001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: package org.apache.xerces.parsers;
019:
020: import java.io.IOException;
021: import java.util.Enumeration;
022: import java.util.Hashtable;
023: import java.util.Locale;
024:
025: import org.apache.xerces.impl.Constants;
026: import org.apache.xerces.impl.XMLEntityManager;
027: import org.apache.xerces.impl.XMLErrorReporter;
028: import org.apache.xerces.util.SymbolTable;
029: import org.apache.xerces.xni.XNIException;
030: import org.apache.xerces.xni.grammars.Grammar;
031: import org.apache.xerces.xni.grammars.XMLGrammarDescription;
032: import org.apache.xerces.xni.grammars.XMLGrammarLoader;
033: import org.apache.xerces.xni.grammars.XMLGrammarPool;
034: import org.apache.xerces.xni.parser.XMLEntityResolver;
035: import org.apache.xerces.xni.parser.XMLErrorHandler;
036: import org.apache.xerces.xni.parser.XMLInputSource;
037:
038: /**
039: * <p> This class provides an easy way for a user to preparse grammars
040: * of various types. By default, it knows how to preparse external
041: * DTD's and schemas; it provides an easy way for user applications to
042: * register classes that know how to parse additional grammar types.
043: * By default, it does no grammar caching; but it provides ways for
044: * user applications to do so.
045: *
046: * @author Neil Graham, IBM
047: *
048: * @version $Id: XMLGrammarPreparser.java 521449 2007-03-22 20:35:42Z mrglavas $
049: */
050: public class XMLGrammarPreparser {
051:
052: //
053: // Constants
054: //
055:
056: // feature: continue-after-fatal-error
057: private final static String CONTINUE_AFTER_FATAL_ERROR = Constants.XERCES_FEATURE_PREFIX
058: + Constants.CONTINUE_AFTER_FATAL_ERROR_FEATURE;
059:
060: /** Property identifier: symbol table. */
061: protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX
062: + Constants.SYMBOL_TABLE_PROPERTY;
063:
064: /** Property identifier: error reporter. */
065: protected static final String ERROR_REPORTER = Constants.XERCES_PROPERTY_PREFIX
066: + Constants.ERROR_REPORTER_PROPERTY;
067:
068: /** Property identifier: error handler. */
069: protected static final String ERROR_HANDLER = Constants.XERCES_PROPERTY_PREFIX
070: + Constants.ERROR_HANDLER_PROPERTY;
071:
072: /** Property identifier: entity resolver. */
073: protected static final String ENTITY_RESOLVER = Constants.XERCES_PROPERTY_PREFIX
074: + Constants.ENTITY_RESOLVER_PROPERTY;
075:
076: /** Property identifier: grammar pool . */
077: protected static final String GRAMMAR_POOL = Constants.XERCES_PROPERTY_PREFIX
078: + Constants.XMLGRAMMAR_POOL_PROPERTY;
079:
080: // the "built-in" grammar loaders
081: private static final Hashtable KNOWN_LOADERS = new Hashtable();
082:
083: static {
084: KNOWN_LOADERS.put(XMLGrammarDescription.XML_SCHEMA,
085: "org.apache.xerces.impl.xs.XMLSchemaLoader");
086: KNOWN_LOADERS.put(XMLGrammarDescription.XML_DTD,
087: "org.apache.xerces.impl.dtd.XMLDTDLoader");
088: }
089:
090: /** Recognized properties. */
091: private static final String[] RECOGNIZED_PROPERTIES = {
092: SYMBOL_TABLE, ERROR_REPORTER, ERROR_HANDLER,
093: ENTITY_RESOLVER, GRAMMAR_POOL, };
094:
095: // Data
096: protected SymbolTable fSymbolTable;
097: protected XMLErrorReporter fErrorReporter;
098: protected XMLEntityResolver fEntityResolver;
099: protected XMLGrammarPool fGrammarPool;
100:
101: protected Locale fLocale;
102:
103: // Hashtable holding our loaders
104: private Hashtable fLoaders;
105:
106: // The number of times the configuration has been modified.
107: private int fModCount = 1;
108:
109: //
110: // Constructors
111: //
112:
113: /** Default constructor. */
114: public XMLGrammarPreparser() {
115: this (new SymbolTable());
116: } // <init>()
117:
118: /**
119: * Constructs a preparser using the specified symbol table.
120: *
121: * @param symbolTable The symbol table to use.
122: */
123: public XMLGrammarPreparser(SymbolTable symbolTable) {
124: fSymbolTable = symbolTable;
125:
126: fLoaders = new Hashtable();
127: setLocale(Locale.getDefault());
128: fErrorReporter = new XMLErrorReporter();
129: fErrorReporter.setLocale(fLocale);
130: fEntityResolver = new XMLEntityManager();
131: // those are all the basic properties...
132: } // <init>(SymbolTable)
133:
134: //
135: // Public methods
136: //
137:
138: /*
139: * Register a type of grammar to make it preparsable. If
140: * the second parameter is null, the parser will use its built-in
141: * facilities for that grammar type.
142: * This should be called by the application immediately
143: * after creating this object and before initializing any properties/features.
144: * @param type URI identifying the type of the grammar
145: * @param loader an object capable of preparsing that type; null if the ppreparser should use built-in knowledge.
146: * @return true if successful; false if no built-in knowledge of
147: * the type or if unable to instantiate the string we know about
148: */
149: public boolean registerPreparser(String grammarType,
150: XMLGrammarLoader loader) {
151: if (loader == null) { // none specified!
152: if (KNOWN_LOADERS.containsKey(grammarType)) {
153: // got one; just instantiate it...
154: String loaderName = (String) KNOWN_LOADERS
155: .get(grammarType);
156: try {
157: ClassLoader cl = ObjectFactory.findClassLoader();
158: XMLGrammarLoader gl = (XMLGrammarLoader) (ObjectFactory
159: .newInstance(loaderName, cl, true));
160: fLoaders.put(grammarType,
161: 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
171: .put(grammarType, new XMLGrammarLoaderContainer(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 is)
192: throws XNIException, IOException {
193: if (fLoaders.containsKey(type)) {
194: XMLGrammarLoaderContainer xglc = (XMLGrammarLoaderContainer) fLoaders
195: .get(type);
196: XMLGrammarLoader gl = xglc.loader;
197: if (xglc.modCount != fModCount) {
198: // make sure gl's been set up with all the "basic" properties:
199: gl.setProperty(SYMBOL_TABLE, fSymbolTable);
200: gl.setProperty(ENTITY_RESOLVER, fEntityResolver);
201: gl.setProperty(ERROR_REPORTER, fErrorReporter);
202: // potentially, not all will support this one...
203: if (fGrammarPool != null) {
204: try {
205: gl.setProperty(GRAMMAR_POOL, fGrammarPool);
206: } catch (Exception e) {
207: // too bad...
208: }
209: }
210: xglc.modCount = fModCount;
211: }
212: return gl.loadGrammar(is);
213: }
214: return null;
215: } // preparseGrammar(String, XMLInputSource): Grammar
216:
217: /**
218: * Set the locale to use for messages.
219: *
220: * @param locale The locale object to use for localization of messages.
221: *
222: * @exception XNIException Thrown if the parser does not support the
223: * specified locale.
224: */
225: public void setLocale(Locale locale) {
226: fLocale = locale;
227: } // setLocale(Locale)
228:
229: /** Return the Locale the XMLGrammarLoader is using. */
230: public Locale getLocale() {
231: return fLocale;
232: } // getLocale(): Locale
233:
234: /**
235: * Sets the error handler.
236: *
237: * @param errorHandler The error handler.
238: */
239: public void setErrorHandler(XMLErrorHandler errorHandler) {
240: fErrorReporter.setProperty(ERROR_HANDLER, errorHandler);
241: } // setErrorHandler(XMLErrorHandler)
242:
243: /** Returns the registered error handler. */
244: public XMLErrorHandler getErrorHandler() {
245: return fErrorReporter.getErrorHandler();
246: } // getErrorHandler(): XMLErrorHandler
247:
248: /**
249: * Sets the entity resolver.
250: *
251: * @param entityResolver The new entity resolver.
252: */
253: public void setEntityResolver(XMLEntityResolver entityResolver) {
254: if (fEntityResolver != entityResolver) {
255: // Overflow. We actually need to reset the
256: // modCount on every XMLGrammarLoaderContainer.
257: if (++fModCount < 0) {
258: clearModCounts();
259: }
260: fEntityResolver = entityResolver;
261: }
262: } // setEntityResolver(XMLEntityResolver)
263:
264: /** Returns the registered entity resolver. */
265: public XMLEntityResolver getEntityResolver() {
266: return fEntityResolver;
267: } // getEntityResolver(): XMLEntityResolver
268:
269: /**
270: * Sets the grammar pool.
271: *
272: * @param grammarPool The new grammar pool.
273: */
274: public void setGrammarPool(XMLGrammarPool grammarPool) {
275: if (fGrammarPool != grammarPool) {
276: // Overflow. We actually need to reset the
277: // modCount on every XMLGrammarLoaderContainer.
278: if (++fModCount < 0) {
279: clearModCounts();
280: }
281: fGrammarPool = grammarPool;
282: }
283: } // setGrammarPool(XMLGrammarPool)
284:
285: /** Returns the registered grammar pool. */
286: public XMLGrammarPool getGrammarPool() {
287: return fGrammarPool;
288: } // getGrammarPool(): XMLGrammarPool
289:
290: // it's possible the application may want access to a certain loader to do
291: // some custom work.
292: public XMLGrammarLoader getLoader(String type) {
293: XMLGrammarLoaderContainer xglc = (XMLGrammarLoaderContainer) fLoaders
294: .get(type);
295: return (xglc != null) ? xglc.loader : null;
296: } // getLoader(String): XMLGrammarLoader
297:
298: // set a feature. This method tries to set it on all
299: // registered loaders; it eats any resulting exceptions. If
300: // an app needs to know if a particular feature is supported
301: // by a grammar loader of a particular type, it will have
302: // to retrieve that loader and use the loader's setFeature method.
303: public void setFeature(String featureId, boolean value) {
304: Enumeration loaders = fLoaders.elements();
305: while (loaders.hasMoreElements()) {
306: XMLGrammarLoader gl = ((XMLGrammarLoaderContainer) loaders
307: .nextElement()).loader;
308: try {
309: gl.setFeature(featureId, value);
310: } catch (Exception e) {
311: // eat it up...
312: }
313: }
314: // since our error reporter is a property we set later,
315: // make sure features it understands are also set.
316: if (featureId.equals(CONTINUE_AFTER_FATAL_ERROR)) {
317: fErrorReporter
318: .setFeature(CONTINUE_AFTER_FATAL_ERROR, value);
319: }
320: } //setFeature(String, boolean)
321:
322: // set a property. This method tries to set it on all
323: // registered loaders; it eats any resulting exceptions. If
324: // an app needs to know if a particular property is supported
325: // by a grammar loader of a particular type, it will have
326: // to retrieve that loader and use the loader's setProperty method.
327: // <p> <strong>An application should use the explicit method
328: // in this class to set "standard" properties like error handler etc.</strong>
329: public void setProperty(String propId, Object value) {
330: Enumeration loaders = fLoaders.elements();
331: while (loaders.hasMoreElements()) {
332: XMLGrammarLoader gl = ((XMLGrammarLoaderContainer) loaders
333: .nextElement()).loader;
334: try {
335: gl.setProperty(propId, value);
336: } catch (Exception e) {
337: // eat it up...
338: }
339: }
340: } //setProperty(String, Object)
341:
342: // get status of feature in a particular loader. This
343: // catches no exceptions--including NPE's--so the application had
344: // better make sure the loader exists and knows about this feature.
345: // @param type type of grammar to look for the feature in.
346: // @param featureId the feature string to query.
347: // @return the value of the feature.
348: public boolean getFeature(String type, String featureId) {
349: XMLGrammarLoader gl = ((XMLGrammarLoaderContainer) fLoaders
350: .get(type)).loader;
351: return gl.getFeature(featureId);
352: } // getFeature (String, String): boolean
353:
354: // get status of property in a particular loader. This
355: // catches no exceptions--including NPE's--so the application had
356: // better make sure the loader exists and knows about this property.
357: // <strong>For standard properties--that will be supported
358: // by all loaders--the specific methods should be queried!</strong>
359: // @param type type of grammar to look for the property in.
360: // @param propertyId the property string to query.
361: // @return the value of the property.
362: public Object getProperty(String type, String propertyId) {
363: XMLGrammarLoader gl = ((XMLGrammarLoaderContainer) fLoaders
364: .get(type)).loader;
365: return gl.getProperty(propertyId);
366: } // getProperty(String, String): Object
367:
368: /**
369: * Container for an XMLGrammarLoader. Also holds the modCount
370: * that the XMLGrammarPreparser had the last time it was used.
371: */
372: static class XMLGrammarLoaderContainer {
373: public final XMLGrammarLoader loader;
374: public int modCount = 0;
375:
376: public XMLGrammarLoaderContainer(XMLGrammarLoader loader) {
377: this .loader = loader;
378: }
379: }
380:
381: private void clearModCounts() {
382: Enumeration loaders = fLoaders.elements();
383: while (loaders.hasMoreElements()) {
384: XMLGrammarLoaderContainer xglc = (XMLGrammarLoaderContainer) loaders
385: .nextElement();
386: xglc.modCount = 0;
387: }
388: fModCount = 1;
389: }
390:
391: } // class XMLGrammarPreparser
|