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.ArrayList;
022: import java.util.HashMap;
023: import java.util.Locale;
024:
025: import org.apache.xerces.impl.Constants;
026: import org.apache.xerces.util.ParserConfigurationSettings;
027: import org.apache.xerces.util.SymbolTable;
028: import org.apache.xerces.xni.XMLDTDContentModelHandler;
029: import org.apache.xerces.xni.XMLDTDHandler;
030: import org.apache.xerces.xni.XMLDocumentHandler;
031: import org.apache.xerces.xni.XNIException;
032: import org.apache.xerces.xni.parser.XMLComponent;
033: import org.apache.xerces.xni.parser.XMLComponentManager;
034: import org.apache.xerces.xni.parser.XMLConfigurationException;
035: import org.apache.xerces.xni.parser.XMLDocumentSource;
036: import org.apache.xerces.xni.parser.XMLEntityResolver;
037: import org.apache.xerces.xni.parser.XMLErrorHandler;
038: import org.apache.xerces.xni.parser.XMLInputSource;
039: import org.apache.xerces.xni.parser.XMLParserConfiguration;
040:
041: /**
042: * A very basic parser configuration. This configuration class can
043: * be used as a base class for custom parser configurations. The
044: * basic parser configuration creates the symbol table (if not
045: * specified at construction time) and manages all of the recognized
046: * features and properties.
047: * <p>
048: * The basic parser configuration does <strong>not</strong> mandate
049: * any particular pipeline configuration or the use of specific
050: * components except for the symbol table. If even this is too much
051: * for a basic parser configuration, the programmer can create a new
052: * configuration class that implements the
053: * <code>XMLParserConfiguration</code> interface.
054: * <p>
055: * Subclasses of the basic parser configuration can add their own
056: * recognized features and properties by calling the
057: * <code>addRecognizedFeature</code> and
058: * <code>addRecognizedProperty</code> methods, respectively.
059: * <p>
060: * The basic parser configuration assumes that the configuration
061: * will be made up of various parser components that implement the
062: * <code>XMLComponent</code> interface. If subclasses of this
063: * configuration create their own components for use in the
064: * parser configuration, then each component should be added to
065: * the list of components by calling the <code>addComponent</code>
066: * method. The basic parser configuration will make sure to call
067: * the <code>reset</code> method of each registered component
068: * before parsing an instance document.
069: * <p>
070: * This class recognizes the following features and properties:
071: * <ul>
072: * <li>Features
073: * <ul>
074: * <li>http://xml.org/sax/features/validation</li>
075: * <li>http://xml.org/sax/features/namespaces</li>
076: * <li>http://xml.org/sax/features/external-general-entities</li>
077: * <li>http://xml.org/sax/features/external-parameter-entities</li>
078: * </ul>
079: * <li>Properties
080: * <ul>
081: * <li>http://xml.org/sax/properties/xml-string</li>
082: * <li>http://apache.org/xml/properties/internal/symbol-table</li>
083: * <li>http://apache.org/xml/properties/internal/error-handler</li>
084: * <li>http://apache.org/xml/properties/internal/entity-resolver</li>
085: * </ul>
086: * </ul>
087: *
088: * @author Arnaud Le Hors, IBM
089: * @author Andy Clark, IBM
090: *
091: * @version $Id: BasicParserConfiguration.java 447239 2006-09-18 05:08:26Z mrglavas $
092: */
093: public abstract class BasicParserConfiguration extends
094: ParserConfigurationSettings implements XMLParserConfiguration {
095:
096: //
097: // Constants
098: //
099:
100: // feature identifiers
101:
102: /** Feature identifier: validation. */
103: protected static final String VALIDATION = Constants.SAX_FEATURE_PREFIX
104: + Constants.VALIDATION_FEATURE;
105:
106: /** Feature identifier: namespaces. */
107: protected static final String NAMESPACES = Constants.SAX_FEATURE_PREFIX
108: + Constants.NAMESPACES_FEATURE;
109:
110: /** Feature identifier: external general entities. */
111: protected static final String EXTERNAL_GENERAL_ENTITIES = Constants.SAX_FEATURE_PREFIX
112: + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE;
113:
114: /** Feature identifier: external parameter entities. */
115: protected static final String EXTERNAL_PARAMETER_ENTITIES = Constants.SAX_FEATURE_PREFIX
116: + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE;
117:
118: // property identifiers
119:
120: /** Property identifier: xml string. */
121: protected static final String XML_STRING = Constants.SAX_PROPERTY_PREFIX
122: + Constants.XML_STRING_PROPERTY;
123:
124: /** Property identifier: symbol table. */
125: protected static final String SYMBOL_TABLE = Constants.XERCES_PROPERTY_PREFIX
126: + Constants.SYMBOL_TABLE_PROPERTY;
127:
128: /** Property identifier: error handler. */
129: protected static final String ERROR_HANDLER = Constants.XERCES_PROPERTY_PREFIX
130: + Constants.ERROR_HANDLER_PROPERTY;
131:
132: /** Property identifier: entity resolver. */
133: protected static final String ENTITY_RESOLVER = Constants.XERCES_PROPERTY_PREFIX
134: + Constants.ENTITY_RESOLVER_PROPERTY;
135:
136: //
137: // Data
138: //
139:
140: // components (non-configurable)
141:
142: /** Symbol table. */
143: protected SymbolTable fSymbolTable;
144:
145: // data
146:
147: /** Locale. */
148: protected Locale fLocale;
149:
150: /** Components. */
151: protected ArrayList fComponents;
152:
153: // handlers
154:
155: /** The document handler. */
156: protected XMLDocumentHandler fDocumentHandler;
157:
158: /** The DTD handler. */
159: protected XMLDTDHandler fDTDHandler;
160:
161: /** The DTD content model handler. */
162: protected XMLDTDContentModelHandler fDTDContentModelHandler;
163:
164: /** Last component in the document pipeline */
165: protected XMLDocumentSource fLastComponent;
166:
167: //
168: // Constructors
169: //
170:
171: /** Default Constructor. */
172: protected BasicParserConfiguration() {
173: this (null, null);
174: } // <init>()
175:
176: /**
177: * Constructs a parser configuration using the specified symbol table.
178: *
179: * @param symbolTable The symbol table to use.
180: */
181: protected BasicParserConfiguration(SymbolTable symbolTable) {
182: this (symbolTable, null);
183: } // <init>(SymbolTable)
184:
185: /**
186: * Constructs a parser configuration using the specified symbol table
187: * and parent settings.
188: *
189: * @param symbolTable The symbol table to use.
190: * @param parentSettings The parent settings.
191: */
192: protected BasicParserConfiguration(SymbolTable symbolTable,
193: XMLComponentManager parentSettings) {
194: super (parentSettings);
195:
196: // create a vector to hold all the components in use
197: fComponents = new ArrayList();
198:
199: // create storage for recognized features and properties
200: fRecognizedFeatures = new ArrayList();
201: fRecognizedProperties = new ArrayList();
202:
203: // create table for features and properties
204: fFeatures = new HashMap();
205: fProperties = new HashMap();
206:
207: // add default recognized features
208: final String[] recognizedFeatures = { PARSER_SETTINGS,
209: VALIDATION, NAMESPACES, EXTERNAL_GENERAL_ENTITIES,
210: EXTERNAL_PARAMETER_ENTITIES, };
211: addRecognizedFeatures(recognizedFeatures);
212: fFeatures.put(PARSER_SETTINGS, Boolean.TRUE);
213: // set state for default features
214: fFeatures.put(VALIDATION, Boolean.FALSE);
215: fFeatures.put(NAMESPACES, Boolean.TRUE);
216: fFeatures.put(EXTERNAL_GENERAL_ENTITIES, Boolean.TRUE);
217: fFeatures.put(EXTERNAL_PARAMETER_ENTITIES, Boolean.TRUE);
218:
219: // add default recognized properties
220: final String[] recognizedProperties = { XML_STRING,
221: SYMBOL_TABLE, ERROR_HANDLER, ENTITY_RESOLVER, };
222: addRecognizedProperties(recognizedProperties);
223:
224: if (symbolTable == null) {
225: symbolTable = new SymbolTable();
226: }
227: fSymbolTable = symbolTable;
228: fProperties.put(SYMBOL_TABLE, fSymbolTable);
229:
230: } // <init>(SymbolTable)
231:
232: /**
233: * Adds a component to the parser configuration. This method will
234: * also add all of the component's recognized features and properties
235: * to the list of default recognized features and properties.
236: *
237: * @param component The component to add.
238: */
239: protected void addComponent(XMLComponent component) {
240:
241: // don't add a component more than once
242: if (fComponents.contains(component)) {
243: return;
244: }
245: fComponents.add(component);
246:
247: // register component's recognized features
248: String[] recognizedFeatures = component.getRecognizedFeatures();
249: addRecognizedFeatures(recognizedFeatures);
250:
251: // register component's recognized properties
252: String[] recognizedProperties = component
253: .getRecognizedProperties();
254: addRecognizedProperties(recognizedProperties);
255:
256: // set default values
257: if (recognizedFeatures != null) {
258: for (int i = 0; i < recognizedFeatures.length; i++) {
259: String featureId = recognizedFeatures[i];
260: Boolean state = component.getFeatureDefault(featureId);
261: if (state != null) {
262: super .setFeature(featureId, state.booleanValue());
263: }
264: }
265: }
266: if (recognizedProperties != null) {
267: for (int i = 0; i < recognizedProperties.length; i++) {
268: String propertyId = recognizedProperties[i];
269: Object value = component.getPropertyDefault(propertyId);
270: if (value != null) {
271: super .setProperty(propertyId, value);
272: }
273: }
274: }
275:
276: } // addComponent(XMLComponent)
277:
278: //
279: // XMLParserConfiguration methods
280: //
281:
282: /**
283: * Parse an XML document.
284: * <p>
285: * The parser can use this method to instruct this configuration
286: * to begin parsing an XML document from any valid input source
287: * (a character stream, a byte stream, or a URI).
288: * <p>
289: * Parsers may not invoke this method while a parse is in progress.
290: * Once a parse is complete, the parser may then parse another XML
291: * document.
292: * <p>
293: * This method is synchronous: it will not return until parsing
294: * has ended. If a client application wants to terminate
295: * parsing early, it should throw an exception.
296: *
297: * @param inputSource The input source for the top-level of the
298: * XML document.
299: *
300: * @exception XNIException Any XNI exception, possibly wrapping
301: * another exception.
302: * @exception IOException An IO exception from the parser, possibly
303: * from a byte stream or character stream
304: * supplied by the parser.
305: */
306: public abstract void parse(XMLInputSource inputSource)
307: throws XNIException, IOException;
308:
309: /**
310: * Sets the document handler on the last component in the pipeline
311: * to receive information about the document.
312: *
313: * @param documentHandler The document handler.
314: */
315: public void setDocumentHandler(XMLDocumentHandler documentHandler) {
316: fDocumentHandler = documentHandler;
317: if (fLastComponent != null) {
318: fLastComponent.setDocumentHandler(fDocumentHandler);
319: if (fDocumentHandler != null) {
320: fDocumentHandler.setDocumentSource(fLastComponent);
321: }
322: }
323: } // setDocumentHandler(XMLDocumentHandler)
324:
325: /** Returns the registered document handler. */
326: public XMLDocumentHandler getDocumentHandler() {
327: return fDocumentHandler;
328: } // getDocumentHandler():XMLDocumentHandler
329:
330: /**
331: * Sets the DTD handler.
332: *
333: * @param dtdHandler The DTD handler.
334: */
335: public void setDTDHandler(XMLDTDHandler dtdHandler) {
336: fDTDHandler = dtdHandler;
337: } // setDTDHandler(XMLDTDHandler)
338:
339: /** Returns the registered DTD handler. */
340: public XMLDTDHandler getDTDHandler() {
341: return fDTDHandler;
342: } // getDTDHandler():XMLDTDHandler
343:
344: /**
345: * Sets the DTD content model handler.
346: *
347: * @param handler The DTD content model handler.
348: */
349: public void setDTDContentModelHandler(
350: XMLDTDContentModelHandler handler) {
351: fDTDContentModelHandler = handler;
352: } // setDTDContentModelHandler(XMLDTDContentModelHandler)
353:
354: /** Returns the registered DTD content model handler. */
355: public XMLDTDContentModelHandler getDTDContentModelHandler() {
356: return fDTDContentModelHandler;
357: } // getDTDContentModelHandler():XMLDTDContentModelHandler
358:
359: /**
360: * Sets the resolver used to resolve external entities. The EntityResolver
361: * interface supports resolution of public and system identifiers.
362: *
363: * @param resolver The new entity resolver. Passing a null value will
364: * uninstall the currently installed resolver.
365: */
366: public void setEntityResolver(XMLEntityResolver resolver) {
367: // REVISIT: Should this be a property?
368: fProperties.put(ENTITY_RESOLVER, resolver);
369: } // setEntityResolver(XMLEntityResolver)
370:
371: /**
372: * Return the current entity resolver.
373: *
374: * @return The current entity resolver, or null if none
375: * has been registered.
376: * @see #setEntityResolver
377: */
378: public XMLEntityResolver getEntityResolver() {
379: // REVISIT: Should this be a property?
380: return (XMLEntityResolver) fProperties.get(ENTITY_RESOLVER);
381: } // getEntityResolver():XMLEntityResolver
382:
383: /**
384: * Allow an application to register an error event handler.
385: *
386: * <p>If the application does not register an error handler, all
387: * error events reported by the SAX parser will be silently
388: * ignored; however, normal processing may not continue. It is
389: * highly recommended that all SAX applications implement an
390: * error handler to avoid unexpected bugs.</p>
391: *
392: * <p>Applications may register a new or different handler in the
393: * middle of a parse, and the SAX parser must begin using the new
394: * handler immediately.</p>
395: *
396: * @param errorHandler The error handler.
397: * @exception java.lang.NullPointerException If the handler
398: * argument is null.
399: * @see #getErrorHandler
400: */
401: public void setErrorHandler(XMLErrorHandler errorHandler) {
402: // REVISIT: Should this be a property?
403: fProperties.put(ERROR_HANDLER, errorHandler);
404: } // setErrorHandler(XMLErrorHandler)
405:
406: /**
407: * Return the current error handler.
408: *
409: * @return The current error handler, or null if none
410: * has been registered.
411: * @see #setErrorHandler
412: */
413: public XMLErrorHandler getErrorHandler() {
414: // REVISIT: Should this be a property?
415: return (XMLErrorHandler) fProperties.get(ERROR_HANDLER);
416: } // getErrorHandler():XMLErrorHandler
417:
418: /**
419: * Set the state of a feature.
420: *
421: * Set the state of any feature in a SAX2 parser. The parser
422: * might not recognize the feature, and if it does recognize
423: * it, it might not be able to fulfill the request.
424: *
425: * @param featureId The unique identifier (URI) of the feature.
426: * @param state The requested state of the feature (true or false).
427: *
428: * @exception org.apache.xerces.xni.parser.XMLConfigurationException If the
429: * requested feature is not known.
430: */
431: public void setFeature(String featureId, boolean state)
432: throws XMLConfigurationException {
433:
434: // forward to every component
435: int count = fComponents.size();
436: for (int i = 0; i < count; i++) {
437: XMLComponent c = (XMLComponent) fComponents.get(i);
438: c.setFeature(featureId, state);
439: }
440: // save state if noone "objects"
441: super .setFeature(featureId, state);
442:
443: } // setFeature(String,boolean)
444:
445: /**
446: * setProperty
447: *
448: * @param propertyId
449: * @param value
450: */
451: public void setProperty(String propertyId, Object value)
452: throws XMLConfigurationException {
453:
454: // forward to every component
455: int count = fComponents.size();
456: for (int i = 0; i < count; i++) {
457: XMLComponent c = (XMLComponent) fComponents.get(i);
458: c.setProperty(propertyId, value);
459: }
460:
461: // store value if noone "objects"
462: super .setProperty(propertyId, value);
463:
464: } // setProperty(String,Object)
465:
466: /**
467: * Set the locale to use for messages.
468: *
469: * @param locale The locale object to use for localization of messages.
470: *
471: * @exception XNIException Thrown if the parser does not support the
472: * specified locale.
473: */
474: public void setLocale(Locale locale) throws XNIException {
475: fLocale = locale;
476: } // setLocale(Locale)
477:
478: /** Returns the locale. */
479: public Locale getLocale() {
480: return fLocale;
481: } // getLocale():Locale
482:
483: //
484: // Protected methods
485: //
486:
487: /**
488: * reset all components before parsing and namespace context
489: */
490: protected void reset() throws XNIException {
491:
492: // reset every component
493: int count = fComponents.size();
494: for (int i = 0; i < count; i++) {
495: XMLComponent c = (XMLComponent) fComponents.get(i);
496: c.reset(this );
497: }
498:
499: } // reset()
500:
501: /**
502: * Check a property. If the property is known and supported, this method
503: * simply returns. Otherwise, the appropriate exception is thrown.
504: *
505: * @param propertyId The unique identifier (URI) of the property
506: * being set.
507: * @exception org.apache.xerces.xni.parser.XMLConfigurationException If the
508: * requested feature is not known or supported.
509: */
510: protected void checkProperty(String propertyId)
511: throws XMLConfigurationException {
512:
513: // special cases
514: if (propertyId.startsWith(Constants.SAX_PROPERTY_PREFIX)) {
515: final int suffixLength = propertyId.length()
516: - Constants.SAX_PROPERTY_PREFIX.length();
517:
518: //
519: // http://xml.org/sax/properties/xml-string
520: // Value type: String
521: // Access: read-only
522: // Get the literal string of characters associated with the
523: // current event. If the parser recognises and supports this
524: // property but is not currently parsing text, it should return
525: // null (this is a good way to check for availability before the
526: // parse begins).
527: //
528: if (suffixLength == Constants.XML_STRING_PROPERTY.length()
529: && propertyId
530: .endsWith(Constants.XML_STRING_PROPERTY)) {
531: // REVISIT - we should probably ask xml-dev for a precise
532: // definition of what this is actually supposed to return, and
533: // in exactly which circumstances.
534: short type = XMLConfigurationException.NOT_SUPPORTED;
535: throw new XMLConfigurationException(type, propertyId);
536: }
537: }
538:
539: // check property
540: super .checkProperty(propertyId);
541:
542: } // checkProperty(String)
543:
544: /**
545: * Check a feature. If feature is know and supported, this method simply
546: * returns. Otherwise, the appropriate exception is thrown.
547: *
548: * @param featureId The unique identifier (URI) of the feature.
549: *
550: * @throws XMLConfigurationException Thrown for configuration error.
551: * In general, components should
552: * only throw this exception if
553: * it is <strong>really</strong>
554: * a critical error.
555: */
556: protected void checkFeature(String featureId)
557: throws XMLConfigurationException {
558:
559: //
560: // Xerces Features
561: //
562: if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
563: final int suffixLength = featureId.length()
564: - Constants.XERCES_FEATURE_PREFIX.length();
565:
566: //
567: // special performance feature: no one by component manager is allowed to set it
568: //
569: if (suffixLength == Constants.PARSER_SETTINGS.length()
570: && featureId.endsWith(Constants.PARSER_SETTINGS)) {
571: short type = XMLConfigurationException.NOT_SUPPORTED;
572: throw new XMLConfigurationException(type, featureId);
573: }
574: }
575:
576: super .checkFeature(featureId);
577:
578: } // checkFeature(String)
579:
580: } // class BasicParserConfiguration
|