1/*
2 * reserved comment block
3 * DO NOT REMOVE OR ALTER!
4 */
5/*
6 * Licensed to the Apache Software Foundation (ASF) under one or more
7 * contributor license agreements.  See the NOTICE file distributed with
8 * this work for additional information regarding copyright ownership.
9 * The ASF licenses this file to You under the Apache License, Version 2.0
10 * (the "License"); you may not use this file except in compliance with
11 * the License.  You may obtain a copy of the License at
12 *
13 *      http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing, software
16 * distributed under the License is distributed on an "AS IS" BASIS,
17 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 * See the License for the specific language governing permissions and
19 * limitations under the License.
20 */
21
22package com.sun.org.apache.xerces.internal.parsers;
23
24import java.io.IOException;
25import java.util.ArrayList;
26import java.util.HashMap;
27import java.util.Locale;
28
29import com.sun.org.apache.xerces.internal.impl.Constants;
30import com.sun.org.apache.xerces.internal.util.FeatureState;
31import com.sun.org.apache.xerces.internal.util.ParserConfigurationSettings;
32import com.sun.org.apache.xerces.internal.util.PropertyState;
33import com.sun.org.apache.xerces.internal.util.Status;
34import com.sun.org.apache.xerces.internal.util.SymbolTable;
35import com.sun.org.apache.xerces.internal.xni.XMLDTDContentModelHandler;
36import com.sun.org.apache.xerces.internal.xni.XMLDTDHandler;
37import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
38import com.sun.org.apache.xerces.internal.xni.XNIException;
39import com.sun.org.apache.xerces.internal.xni.parser.XMLComponent;
40import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
41import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
42import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
43import com.sun.org.apache.xerces.internal.xni.parser.XMLEntityResolver;
44import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler;
45import com.sun.org.apache.xerces.internal.xni.parser.XMLInputSource;
46import com.sun.org.apache.xerces.internal.xni.parser.XMLParserConfiguration;
47
48/**
49 * A very basic parser configuration. This configuration class can
50 * be used as a base class for custom parser configurations. The
51 * basic parser configuration creates the symbol table (if not
52 * specified at construction time) and manages all of the recognized
53 * features and properties.
54 * <p>
55 * The basic parser configuration does <strong>not</strong> mandate
56 * any particular pipeline configuration or the use of specific
57 * components except for the symbol table. If even this is too much
58 * for a basic parser configuration, the programmer can create a new
59 * configuration class that implements the
60 * <code>XMLParserConfiguration</code> interface.
61 * <p>
62 * Subclasses of the basic parser configuration can add their own
63 * recognized features and properties by calling the
64 * <code>addRecognizedFeature</code> and
65 * <code>addRecognizedProperty</code> methods, respectively.
66 * <p>
67 * The basic parser configuration assumes that the configuration
68 * will be made up of various parser components that implement the
69 * <code>XMLComponent</code> interface. If subclasses of this
70 * configuration create their own components for use in the
71 * parser configuration, then each component should be added to
72 * the list of components by calling the <code>addComponent</code>
73 * method. The basic parser configuration will make sure to call
74 * the <code>reset</code> method of each registered component
75 * before parsing an instance document.
76 * <p>
77 * This class recognizes the following features and properties:
78 * <ul>
79 * <li>Features
80 *  <ul>
81 *   <li>http://xml.org/sax/features/validation</li>
82 *   <li>http://xml.org/sax/features/namespaces</li>
83 *   <li>http://xml.org/sax/features/external-general-entities</li>
84 *   <li>http://xml.org/sax/features/external-parameter-entities</li>
85 *  </ul>
86 * <li>Properties
87 *  <ul>
88 *   <li>http://xml.org/sax/properties/xml-string</li>
89 *   <li>http://apache.org/xml/properties/internal/symbol-table</li>
90 *   <li>http://apache.org/xml/properties/internal/error-handler</li>
91 *   <li>http://apache.org/xml/properties/internal/entity-resolver</li>
92 *  </ul>
93 * </ul>
94 *
95 * @author Arnaud  Le Hors, IBM
96 * @author Andy Clark, IBM
97 *
98 */
99public abstract class BasicParserConfiguration
100    extends ParserConfigurationSettings
101    implements XMLParserConfiguration {
102
103    //
104    // Constants
105    //
106
107    // feature identifiers
108
109    /** Feature identifier: validation. */
110    protected static final String VALIDATION =
111        Constants.SAX_FEATURE_PREFIX + Constants.VALIDATION_FEATURE;
112
113    /** Feature identifier: namespaces. */
114    protected static final String NAMESPACES =
115        Constants.SAX_FEATURE_PREFIX + Constants.NAMESPACES_FEATURE;
116
117    /** Feature identifier: external general entities. */
118    protected static final String EXTERNAL_GENERAL_ENTITIES =
119        Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_GENERAL_ENTITIES_FEATURE;
120
121    /** Feature identifier: external parameter entities. */
122    protected static final String EXTERNAL_PARAMETER_ENTITIES =
123        Constants.SAX_FEATURE_PREFIX + Constants.EXTERNAL_PARAMETER_ENTITIES_FEATURE;
124
125    // property identifiers
126
127    /** Property identifier: xml string. */
128    protected static final String XML_STRING =
129        Constants.SAX_PROPERTY_PREFIX + Constants.XML_STRING_PROPERTY;
130
131    /** Property identifier: symbol table. */
132    protected static final String SYMBOL_TABLE =
133        Constants.XERCES_PROPERTY_PREFIX + Constants.SYMBOL_TABLE_PROPERTY;
134
135    /** Property identifier: error handler. */
136    protected static final String ERROR_HANDLER =
137        Constants.XERCES_PROPERTY_PREFIX + Constants.ERROR_HANDLER_PROPERTY;
138
139    /** Property identifier: entity resolver. */
140    protected static final String ENTITY_RESOLVER =
141        Constants.XERCES_PROPERTY_PREFIX + Constants.ENTITY_RESOLVER_PROPERTY;
142
143    //
144    // Data
145    //
146
147    // components (non-configurable)
148
149    /** Symbol table. */
150    protected SymbolTable fSymbolTable;
151
152
153    // data
154
155    /** Locale. */
156    protected Locale fLocale;
157
158    /** Components. */
159    protected ArrayList fComponents;
160
161    // handlers
162
163    /** The document handler. */
164    protected XMLDocumentHandler fDocumentHandler;
165
166    /** The DTD handler. */
167    protected XMLDTDHandler fDTDHandler;
168
169    /** The DTD content model handler. */
170    protected XMLDTDContentModelHandler fDTDContentModelHandler;
171
172    /** Last component in the document pipeline */
173    protected XMLDocumentSource fLastComponent;
174
175    //
176    // Constructors
177    //
178
179    /** Default Constructor. */
180    protected BasicParserConfiguration() {
181        this(null, null);
182    } // <init>()
183
184    /**
185     * Constructs a parser configuration using the specified symbol table.
186     *
187     * @param symbolTable The symbol table to use.
188     */
189    protected BasicParserConfiguration(SymbolTable symbolTable) {
190        this(symbolTable, null);
191    } // <init>(SymbolTable)
192
193    /**
194     * Constructs a parser configuration using the specified symbol table
195     * and parent settings.
196     *
197     * @param symbolTable    The symbol table to use.
198     * @param parentSettings The parent settings.
199     */
200    protected BasicParserConfiguration(SymbolTable symbolTable,
201                                       XMLComponentManager parentSettings) {
202        super(parentSettings);
203
204        // create a vector to hold all the components in use
205        fComponents = new ArrayList();
206
207        // create table for features and properties
208        fFeatures = new HashMap();
209        fProperties = new HashMap();
210
211        // add default recognized features
212        final String[] recognizedFeatures = {
213                PARSER_SETTINGS,
214            VALIDATION,
215            NAMESPACES,
216            EXTERNAL_GENERAL_ENTITIES,
217            EXTERNAL_PARAMETER_ENTITIES,
218        };
219        addRecognizedFeatures(recognizedFeatures);
220        fFeatures.put(PARSER_SETTINGS, Boolean.TRUE);
221        // set state for default features
222                fFeatures.put(VALIDATION, Boolean.FALSE);
223                fFeatures.put(NAMESPACES, Boolean.TRUE);
224                fFeatures.put(EXTERNAL_GENERAL_ENTITIES, Boolean.TRUE);
225                fFeatures.put(EXTERNAL_PARAMETER_ENTITIES, Boolean.TRUE);
226
227        // add default recognized properties
228        final String[] recognizedProperties = {
229            XML_STRING,
230            SYMBOL_TABLE,
231            ERROR_HANDLER,
232            ENTITY_RESOLVER,
233        };
234        addRecognizedProperties(recognizedProperties);
235
236        if (symbolTable == null) {
237            symbolTable = new SymbolTable();
238        }
239        fSymbolTable = symbolTable;
240        fProperties.put(SYMBOL_TABLE, fSymbolTable);
241
242    } // <init>(SymbolTable)
243
244    /**
245     * Adds a component to the parser configuration. This method will
246     * also add all of the component's recognized features and properties
247     * to the list of default recognized features and properties.
248     *
249     * @param component The component to add.
250     */
251    protected void addComponent(XMLComponent component) {
252
253        // don't add a component more than once
254        if (fComponents.contains(component)) {
255            return;
256        }
257        fComponents.add(component);
258
259        // register component's recognized features
260        String[] recognizedFeatures = component.getRecognizedFeatures();
261        addRecognizedFeatures(recognizedFeatures);
262
263        // register component's recognized properties
264        String[] recognizedProperties = component.getRecognizedProperties();
265        addRecognizedProperties(recognizedProperties);
266
267        // set default values
268        if (recognizedFeatures != null) {
269            for (int i = 0; i < recognizedFeatures.length; i++) {
270                String featureId = recognizedFeatures[i];
271                Boolean state = component.getFeatureDefault(featureId);
272                if (state != null) {
273                    super.setFeature(featureId, state.booleanValue());
274                }
275            }
276        }
277        if (recognizedProperties != null) {
278            for (int i = 0; i < recognizedProperties.length; i++) {
279                String propertyId = recognizedProperties[i];
280                Object value = component.getPropertyDefault(propertyId);
281                if (value != null) {
282                    super.setProperty(propertyId, value);
283                }
284            }
285        }
286
287    } // addComponent(XMLComponent)
288
289    //
290    // XMLParserConfiguration methods
291    //
292
293    /**
294     * Parse an XML document.
295     * <p>
296     * The parser can use this method to instruct this configuration
297     * to begin parsing an XML document from any valid input source
298     * (a character stream, a byte stream, or a URI).
299     * <p>
300     * Parsers may not invoke this method while a parse is in progress.
301     * Once a parse is complete, the parser may then parse another XML
302     * document.
303     * <p>
304     * This method is synchronous: it will not return until parsing
305     * has ended.  If a client application wants to terminate
306     * parsing early, it should throw an exception.
307     *
308     * @param inputSource The input source for the top-level of the
309     *               XML document.
310     *
311     * @exception XNIException Any XNI exception, possibly wrapping
312     *                         another exception.
313     * @exception IOException  An IO exception from the parser, possibly
314     *                         from a byte stream or character stream
315     *                         supplied by the parser.
316     */
317    public abstract void parse(XMLInputSource inputSource)
318        throws XNIException, IOException;
319
320    /**
321     * Sets the document handler on the last component in the pipeline
322     * to receive information about the document.
323     *
324     * @param documentHandler   The document handler.
325     */
326    public void setDocumentHandler(XMLDocumentHandler documentHandler) {
327        fDocumentHandler = documentHandler;
328        if (fLastComponent != null) {
329            fLastComponent.setDocumentHandler(fDocumentHandler);
330            if (fDocumentHandler !=null){
331                fDocumentHandler.setDocumentSource(fLastComponent);
332            }
333        }
334    } // setDocumentHandler(XMLDocumentHandler)
335
336    /** Returns the registered document handler. */
337    public XMLDocumentHandler getDocumentHandler() {
338        return fDocumentHandler;
339    } // getDocumentHandler():XMLDocumentHandler
340
341    /**
342     * Sets the DTD handler.
343     *
344     * @param dtdHandler The DTD handler.
345     */
346    public void setDTDHandler(XMLDTDHandler dtdHandler) {
347        fDTDHandler = dtdHandler;
348    } // setDTDHandler(XMLDTDHandler)
349
350    /** Returns the registered DTD handler. */
351    public XMLDTDHandler getDTDHandler() {
352        return fDTDHandler;
353    } // getDTDHandler():XMLDTDHandler
354
355    /**
356     * Sets the DTD content model handler.
357     *
358     * @param handler The DTD content model handler.
359     */
360    public void setDTDContentModelHandler(XMLDTDContentModelHandler handler) {
361        fDTDContentModelHandler = handler;
362    } // setDTDContentModelHandler(XMLDTDContentModelHandler)
363
364    /** Returns the registered DTD content model handler. */
365    public XMLDTDContentModelHandler getDTDContentModelHandler() {
366        return fDTDContentModelHandler;
367    } // getDTDContentModelHandler():XMLDTDContentModelHandler
368
369    /**
370     * Sets the resolver used to resolve external entities. The EntityResolver
371     * interface supports resolution of public and system identifiers.
372     *
373     * @param resolver The new entity resolver. Passing a null value will
374     *                 uninstall the currently installed resolver.
375     */
376    public void setEntityResolver(XMLEntityResolver resolver) {
377        // REVISIT: Should this be a property?
378        fProperties.put(ENTITY_RESOLVER, resolver);
379    } // setEntityResolver(XMLEntityResolver)
380
381    /**
382     * Return the current entity resolver.
383     *
384     * @return The current entity resolver, or null if none
385     *         has been registered.
386     * @see #setEntityResolver
387     */
388    public XMLEntityResolver getEntityResolver() {
389        // REVISIT: Should this be a property?
390        return (XMLEntityResolver)fProperties.get(ENTITY_RESOLVER);
391    } // getEntityResolver():XMLEntityResolver
392
393    /**
394     * Allow an application to register an error event handler.
395     *
396     * <p>If the application does not register an error handler, all
397     * error events reported by the SAX parser will be silently
398     * ignored; however, normal processing may not continue.  It is
399     * highly recommended that all SAX applications implement an
400     * error handler to avoid unexpected bugs.</p>
401     *
402     * <p>Applications may register a new or different handler in the
403     * middle of a parse, and the SAX parser must begin using the new
404     * handler immediately.</p>
405     *
406     * @param errorHandler The error handler.
407     * @exception java.lang.NullPointerException If the handler
408     *            argument is null.
409     * @see #getErrorHandler
410     */
411    public void setErrorHandler(XMLErrorHandler errorHandler) {
412        // REVISIT: Should this be a property?
413        fProperties.put(ERROR_HANDLER, errorHandler);
414    } // setErrorHandler(XMLErrorHandler)
415
416    /**
417     * Return the current error handler.
418     *
419     * @return The current error handler, or null if none
420     *         has been registered.
421     * @see #setErrorHandler
422     */
423    public XMLErrorHandler getErrorHandler() {
424        // REVISIT: Should this be a property?
425        return (XMLErrorHandler)fProperties.get(ERROR_HANDLER);
426    } // getErrorHandler():XMLErrorHandler
427
428    /**
429     * Set the state of a feature.
430     *
431     * Set the state of any feature in a SAX2 parser.  The parser
432     * might not recognize the feature, and if it does recognize
433     * it, it might not be able to fulfill the request.
434     *
435     * @param featureId The unique identifier (URI) of the feature.
436     * @param state The requested state of the feature (true or false).
437     *
438     * @exception com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException If the
439     *            requested feature is not known.
440     */
441    public void setFeature(String featureId, boolean state)
442        throws XMLConfigurationException {
443
444        // forward to every component
445        int count = fComponents.size();
446        for (int i = 0; i < count; i++) {
447            XMLComponent c = (XMLComponent) fComponents.get(i);
448            c.setFeature(featureId, state);
449        }
450        // save state if noone "objects"
451        super.setFeature(featureId, state);
452
453    } // setFeature(String,boolean)
454
455    /**
456     * setProperty
457     *
458     * @param propertyId
459     * @param value
460     */
461    public void setProperty(String propertyId, Object value)
462        throws XMLConfigurationException {
463
464        // forward to every component
465        int count = fComponents.size();
466        for (int i = 0; i < count; i++) {
467            XMLComponent c = (XMLComponent) fComponents.get(i);
468            c.setProperty(propertyId, value);
469        }
470
471        // store value if noone "objects"
472        super.setProperty(propertyId, value);
473
474    } // setProperty(String,Object)
475
476    /**
477     * Set the locale to use for messages.
478     *
479     * @param locale The locale object to use for localization of messages.
480     *
481     * @exception XNIException Thrown if the parser does not support the
482     *                         specified locale.
483     */
484    public void setLocale(Locale locale) throws XNIException {
485        fLocale = locale;
486    } // setLocale(Locale)
487
488    /** Returns the locale. */
489    public Locale getLocale() {
490        return fLocale;
491    } // getLocale():Locale
492
493    //
494    // Protected methods
495    //
496
497    /**
498     * reset all components before parsing and namespace context
499     */
500    protected void reset() throws XNIException {
501
502        // reset every component
503        int count = fComponents.size();
504        for (int i = 0; i < count; i++) {
505            XMLComponent c = (XMLComponent) fComponents.get(i);
506            c.reset(this);
507        }
508
509    } // reset()
510
511    /**
512     * Check a property. If the property is known and supported, this method
513     * simply returns. Otherwise, the appropriate exception is thrown.
514     *
515     * @param propertyId The unique identifier (URI) of the property
516     *                   being set.
517     * @exception com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException If the
518     *            requested feature is not known or supported.
519     */
520    protected PropertyState checkProperty(String propertyId)
521        throws XMLConfigurationException {
522
523        // special cases
524        if (propertyId.startsWith(Constants.SAX_PROPERTY_PREFIX)) {
525            final int suffixLength = propertyId.length() - Constants.SAX_PROPERTY_PREFIX.length();
526
527            //
528            // http://xml.org/sax/properties/xml-string
529            // Value type: String
530            // Access: read-only
531            //   Get the literal string of characters associated with the
532            //   current event.  If the parser recognises and supports this
533            //   property but is not currently parsing text, it should return
534            //   null (this is a good way to check for availability before the
535            //   parse begins).
536            //
537            if (suffixLength == Constants.XML_STRING_PROPERTY.length() &&
538                propertyId.endsWith(Constants.XML_STRING_PROPERTY)) {
539                // REVISIT - we should probably ask xml-dev for a precise
540                // definition of what this is actually supposed to return, and
541                // in exactly which circumstances.
542                return PropertyState.NOT_SUPPORTED;
543            }
544        }
545
546        // check property
547        return super.checkProperty(propertyId);
548
549    } // checkProperty(String)
550
551
552    /**
553     * Check a feature. If feature is know and supported, this method simply
554     * returns. Otherwise, the appropriate exception is thrown.
555     *
556     * @param featureId The unique identifier (URI) of the feature.
557     *
558     * @throws XMLConfigurationException Thrown for configuration error.
559     *                                   In general, components should
560     *                                   only throw this exception if
561     *                                   it is <strong>really</strong>
562     *                                   a critical error.
563     */
564    protected FeatureState checkFeature(String featureId)
565        throws XMLConfigurationException {
566
567        //
568        // Xerces Features
569        //
570        if (featureId.startsWith(Constants.XERCES_FEATURE_PREFIX)) {
571            final int suffixLength = featureId.length() - Constants.XERCES_FEATURE_PREFIX.length();
572
573            //
574            // special performance feature: no one by component manager is allowed to set it
575            //
576            if (suffixLength == Constants.PARSER_SETTINGS.length() &&
577                featureId.endsWith(Constants.PARSER_SETTINGS)) {
578                return FeatureState.NOT_SUPPORTED;
579            }
580        }
581
582        return super.checkFeature(featureId);
583     } // checkFeature(String)
584
585
586} // class BasicParserConfiguration
587