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 xni.parser;
19 
20 import java.io.FileInputStream;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.net.MalformedURLException;
24 import java.net.URL;
25 import java.util.Hashtable;
26 import java.util.Locale;
27 import java.util.Vector;
28 
29 import org.apache.xerces.xni.XMLDTDContentModelHandler;
30 import org.apache.xerces.xni.XMLDTDHandler;
31 import org.apache.xerces.xni.XMLDocumentHandler;
32 import org.apache.xerces.xni.XNIException;
33 import org.apache.xerces.xni.parser.XMLComponent;
34 import org.apache.xerces.xni.parser.XMLConfigurationException;
35 import org.apache.xerces.xni.parser.XMLEntityResolver;
36 import org.apache.xerces.xni.parser.XMLErrorHandler;
37 import org.apache.xerces.xni.parser.XMLInputSource;
38 import org.apache.xerces.xni.parser.XMLParserConfiguration;
39 
40 /**
41  * This abstract parser configuration simply helps manage components,
42  * features and properties, and other tasks common to all parser
43  * configurations. In order to subclass this configuration and use
44  * it effectively, the subclass is required to do the following:
45  * <ul>
46  * <li>
47  *  Add all configurable components using the <code>addComponent</code>
48  *  method,</li>
49  * <li>Implement the <code>parse</code> method, and</li>
50  * <li>Call the <code>resetComponents</code> before parsing.</li>
51  * </ul>
52  *
53  * @author Andy Clark, IBM
54  *
55  * @version $Id: AbstractConfiguration.java 699895 2008-09-28 21:21:24Z mrglavas $
56  */
57 public abstract class AbstractConfiguration
58     implements XMLParserConfiguration {
59 
60     //
61     // Data
62     //
63 
64     // features and properties
65 
66     /** Recognized features. */
67     protected final Vector fRecognizedFeatures = new Vector();
68 
69     /** Recognized properties. */
70     protected final Vector fRecognizedProperties = new Vector();
71 
72     /** Features. */
73     protected final Hashtable fFeatures = new Hashtable();
74 
75     /** Properties. */
76     protected final Hashtable fProperties = new Hashtable();
77 
78     // other parser configuration fields
79 
80     /** The registered entity resolver. */
81     protected XMLEntityResolver fEntityResolver;
82 
83     /** The registered error handler. */
84     protected XMLErrorHandler fErrorHandler;
85 
86     /** The registered document handler. */
87     protected XMLDocumentHandler fDocumentHandler;
88 
89     /** The registered DTD handler. */
90     protected XMLDTDHandler fDTDHandler;
91 
92     /** The registered DTD content model handler. */
93     protected XMLDTDContentModelHandler fDTDContentModelHandler;
94 
95     /** Locale for error messages. */
96     protected Locale fLocale;
97 
98     // components
99 
100     /** List of configurable components. */
101     protected final Vector fComponents = new Vector();
102 
103     //
104     // XMLParserConfiguration methods
105     //
106 
107     /**
108      * Allows a parser to add parser specific features to be recognized
109      * and managed by the parser configuration.
110      *
111      * @param featureIds An array of the additional feature identifiers
112      *                   to be recognized.
113      */
addRecognizedFeatures(String[] featureIds)114     public void addRecognizedFeatures(String[] featureIds) {
115         int length = featureIds != null ? featureIds.length : 0;
116         for (int i = 0; i < length; i++) {
117             String featureId = featureIds[i];
118             if (!fRecognizedFeatures.contains(featureId)) {
119                 fRecognizedFeatures.addElement(featureId);
120             }
121         }
122     } // addRecognizedFeatures(String[])
123 
124     /**
125      * Sets the state of a feature. This method is called by the parser
126      * and gets propagated to components in this parser configuration.
127      *
128      * @param featureId The feature identifier.
129      * @param state     The state of the feature.
130      *
131      * @throws XMLConfigurationException Thrown if there is a configuration
132      *                                   error.
133      */
setFeature(String featureId, boolean state)134     public void setFeature(String featureId, boolean state)
135         throws XMLConfigurationException {
136         if (!fRecognizedFeatures.contains(featureId)) {
137             short type = XMLConfigurationException.NOT_RECOGNIZED;
138             throw new XMLConfigurationException(type, featureId);
139         }
140         fFeatures.put(featureId, state ? Boolean.TRUE : Boolean.FALSE);
141         int length = fComponents.size();
142         for (int i = 0; i < length; i++) {
143             XMLComponent component = (XMLComponent)fComponents.elementAt(i);
144             component.setFeature(featureId, state);
145         }
146     } // setFeature(String,boolean)
147 
148     /**
149      * Returns the state of a feature.
150      *
151      * @param featureId The feature identifier.
152      *
153      * @throws XMLConfigurationException Thrown if there is a configuration
154      *                                   error.
155      */
getFeature(String featureId)156     public boolean getFeature(String featureId)
157         throws XMLConfigurationException {
158         if (!fRecognizedFeatures.contains(featureId)) {
159             short type = XMLConfigurationException.NOT_RECOGNIZED;
160             throw new XMLConfigurationException(type, featureId);
161         }
162         Boolean state = (Boolean)fFeatures.get(featureId);
163         return state != null ? state.booleanValue() : false;
164     } // getFeature(String):boolean
165 
166     /**
167      * Allows a parser to add parser specific properties to be recognized
168      * and managed by the parser configuration.
169      *
170      * @param propertyIds An array of the additional property identifiers
171      *                    to be recognized.
172      */
addRecognizedProperties(String[] propertyIds)173     public void addRecognizedProperties(String[] propertyIds) {
174         int length = propertyIds != null ? propertyIds.length : 0;
175         for (int i = 0; i < length; i++) {
176             String propertyId = propertyIds[i];
177             if (!fRecognizedProperties.contains(propertyId)) {
178                 fRecognizedProperties.addElement(propertyId);
179             }
180         }
181     } // addRecognizedProperties(String[])
182 
183     /**
184      * Sets the value of a property. This method is called by the parser
185      * and gets propagated to components in this parser configuration.
186      *
187      * @param propertyId The property identifier.
188      * @param value      The value of the property.
189      *
190      * @throws XMLConfigurationException Thrown if there is a configuration
191      *                                   error.
192      */
setProperty(String propertyId, Object value)193     public void setProperty(String propertyId, Object value)
194         throws XMLConfigurationException {
195         if (!fRecognizedProperties.contains(propertyId)) {
196             short type = XMLConfigurationException.NOT_RECOGNIZED;
197             throw new XMLConfigurationException(type, propertyId);
198         }
199         if (value != null) {
200             fProperties.put(propertyId, value);
201         }
202         else {
203             fProperties.remove(propertyId);
204         }
205         int length = fComponents.size();
206         for (int i = 0; i < length; i++) {
207             XMLComponent component = (XMLComponent)fComponents.elementAt(i);
208             component.setProperty(propertyId, value);
209         }
210     } // setProperty(String,Object)
211 
212     /**
213      * Returns the value of a property.
214      *
215      * @param propertyId The property identifier.
216      *
217      * @throws XMLConfigurationException Thrown if there is a configuration
218      *                                   error.
219      */
getProperty(String propertyId)220     public Object getProperty(String propertyId)
221         throws XMLConfigurationException {
222         if (!fRecognizedProperties.contains(propertyId)) {
223             short type = XMLConfigurationException.NOT_RECOGNIZED;
224             throw new XMLConfigurationException(type, propertyId);
225         }
226         Object value = fProperties.get(propertyId);
227         return value;
228     } // getProperty(String):Object
229 
230     /**
231      * Sets the entity resolver.
232      *
233      * @param resolver The new entity resolver.
234      */
setEntityResolver(XMLEntityResolver resolver)235     public void setEntityResolver(XMLEntityResolver resolver) {
236         fEntityResolver = resolver;
237     } // setEntityResolver(XMLEntityResolver)
238 
239     /** Returns the registered entity resolver. */
getEntityResolver()240     public XMLEntityResolver getEntityResolver() {
241         return fEntityResolver;
242     } // getEntityResolver():XMLEntityResolver
243 
244     /**
245      * Sets the error handler.
246      *
247      * @param handler The error resolver.
248      */
setErrorHandler(XMLErrorHandler handler)249     public void setErrorHandler(XMLErrorHandler handler) {
250         fErrorHandler = handler;
251     } // setErrorHandler(XMLErrorHandler)
252 
253     /** Returns the registered error handler. */
getErrorHandler()254     public XMLErrorHandler getErrorHandler() {
255         return fErrorHandler;
256     } // getErrorHandler():XMLErrorHandler
257 
258     /**
259      * Sets the document handler to receive information about the document.
260      *
261      * @param handler The document handler.
262      */
setDocumentHandler(XMLDocumentHandler handler)263     public void setDocumentHandler(XMLDocumentHandler handler) {
264         fDocumentHandler = handler;
265     } // setDocumentHandler(XMLDocumentHandler)
266 
267     /** Returns the registered document handler. */
getDocumentHandler()268     public XMLDocumentHandler getDocumentHandler() {
269         return fDocumentHandler;
270     } // getDocumentHandler():XMLDocumentHandler
271 
272     /**
273      * Sets the DTD handler.
274      *
275      * @param handler The DTD handler.
276      */
setDTDHandler(XMLDTDHandler handler)277     public void setDTDHandler(XMLDTDHandler handler) {
278         fDTDHandler = handler;
279     } // setDTDHandler(XMLDTDHandler)
280 
281     /** Returns the registered DTD handler. */
getDTDHandler()282     public XMLDTDHandler getDTDHandler() {
283         return fDTDHandler;
284     } // getDTDHandler():XMLDTDHandler
285 
286     /**
287      * Sets the DTD content model handler.
288      *
289      * @param handler The DTD content model handler.
290      */
setDTDContentModelHandler(XMLDTDContentModelHandler handler)291     public void setDTDContentModelHandler(XMLDTDContentModelHandler handler) {
292         fDTDContentModelHandler = handler;
293     } // setDTDContentModelHandler(XMLDTDContentModelHandler)
294 
295     /** Returns the registered DTD content model handler. */
getDTDContentModelHandler()296     public XMLDTDContentModelHandler getDTDContentModelHandler() {
297         return fDTDContentModelHandler;
298     } // getDTDContentModelHandler():XMLDTDContentModelHandler
299 
300     /**
301      * Parse an XML document.
302      * <p>
303      * The parser can use this method to instruct this configuration
304      * to begin parsing an XML document from any valid input source
305      * (a character stream, a byte stream, or a URI).
306      * <p>
307      * Parsers may not invoke this method while a parse is in progress.
308      * Once a parse is complete, the parser may then parse another XML
309      * document.
310      * <p>
311      * This method is synchronous: it will not return until parsing
312      * has ended.  If a client application wants to terminate
313      * parsing early, it should throw an exception.
314      * <p>
315      * <strong>Note:</strong> This method needs to be implemented
316      * by the subclass.
317      *
318      * @param source The input source for the top-level of the
319      *               XML document.
320      *
321      * @exception XNIException Any XNI exception, possibly wrapping
322      *                         another exception.
323      * @exception IOException  An IO exception from the parser, possibly
324      *                         from a byte stream or character stream
325      *                         supplied by the parser.
326      */
parse(XMLInputSource inputSource)327     public abstract void parse(XMLInputSource inputSource)
328         throws IOException, XNIException;
329 
330     /**
331      * Set the locale to use for messages.
332      *
333      * @param locale The locale object to use for localization of messages.
334      *
335      * @exception XNIException Thrown if the parser does not support the
336      *                         specified locale.
337      */
setLocale(Locale locale)338     public void setLocale(Locale locale) {
339         fLocale = locale;
340     } // setLocale(Locale)
341 
342 
343     /** Returns the locale. */
getLocale()344     public Locale getLocale() {
345         return fLocale;
346     } // getLocale():Locale
347 
348     //
349     // Protected methods
350     //
351 
352     /**
353      * Adds a component to list of configurable components. If the
354      * same component is added multiple times, the component is
355      * added only the first time.
356      * <p>
357      * This method helps manage the components in the configuration.
358      * Therefore, all subclasses should call this method to add the
359      * components specific to the configuration.
360      *
361      * @param component The component to add.
362      *
363      * @see #resetComponents
364      */
addComponent(XMLComponent component)365     protected void addComponent(XMLComponent component) {
366         if (!fComponents.contains(component)) {
367             fComponents.addElement(component);
368             addRecognizedFeatures(component.getRecognizedFeatures());
369             addRecognizedProperties(component.getRecognizedProperties());
370         }
371     } // addComponent(XMLComponent)
372 
373     /**
374      * Resets all of the registered components. Before the subclassed
375      * configuration begins parsing, it should call this method to
376      * reset the components.
377      *
378      * @see #addComponent
379      */
resetComponents()380     protected void resetComponents()
381         throws XMLConfigurationException {
382         int length = fComponents.size();
383         for (int i = 0; i < length; i++) {
384             XMLComponent component = (XMLComponent)fComponents.elementAt(i);
385             component.reset(this);
386         }
387     } // resetComponents()
388 
389     /**
390      * This method tries to open the necessary stream for the given
391      * XMLInputSource. If the input source already has a character
392      * stream (java.io.Reader) or a byte stream (java.io.InputStream)
393      * set, this method returns immediately. However, if no character
394      * or byte stream is already open, this method attempts to open
395      * an input stream using the source's system identifier.
396      *
397      * @param source The input source to open.
398      */
openInputSourceStream(XMLInputSource source)399     protected void openInputSourceStream(XMLInputSource source)
400         throws IOException {
401         if (source.getCharacterStream() != null) {
402             return;
403         }
404         InputStream stream = source.getByteStream();
405         if (stream == null) {
406             String systemId = source.getSystemId();
407             try {
408                 URL url = new URL(systemId);
409                 stream = url.openStream();
410             }
411             catch (MalformedURLException e) {
412                 stream = new FileInputStream(systemId);
413             }
414             source.setByteStream(stream);
415         }
416     } // openInputSourceStream(XMLInputSource)
417 
418 } // class AbstractConfiguration
419