View Javadoc
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.commons.configuration2;
19  
20  import java.io.IOException;
21  
22  import org.xml.sax.Attributes;
23  import org.xml.sax.ContentHandler;
24  import org.xml.sax.DTDHandler;
25  import org.xml.sax.EntityResolver;
26  import org.xml.sax.ErrorHandler;
27  import org.xml.sax.InputSource;
28  import org.xml.sax.SAXException;
29  import org.xml.sax.XMLReader;
30  import org.xml.sax.helpers.AttributesImpl;
31  
32  /**
33   * <p>
34   * A base class for &quot;faked&quot; {@code XMLReader} classes that transform a configuration object in a set of SAX
35   * parsing events.
36   * </p>
37   * <p>
38   * This class provides dummy implementations for most of the methods defined in the {@code XMLReader} interface that are
39   * not used for this special purpose. There will be concrete sub classes that process specific configuration classes.
40   * </p>
41   */
42  public abstract class ConfigurationXMLReader implements XMLReader {
43      /** Constant for the namespace URI. */
44      protected static final String NS_URI = "";
45  
46      /** Constant for the default name of the root element. */
47      private static final String DEFAULT_ROOT_NAME = "config";
48  
49      /** An empty attributes object. */
50      private static final Attributes EMPTY_ATTRS = new AttributesImpl();
51  
52      /** Stores the content handler. */
53      private ContentHandler contentHandler;
54  
55      /** Stores an exception that occurred during parsing. */
56      private SAXException exception;
57  
58      /** Stores the name for the root element. */
59      private String rootName;
60  
61      /**
62       * Creates a new instance of {@code ConfigurationXMLReader}.
63       */
64      protected ConfigurationXMLReader() {
65          rootName = DEFAULT_ROOT_NAME;
66      }
67  
68      /**
69       * Parses the current configuration object. The passed system ID will be ignored.
70       *
71       * @param systemId the system ID (ignored)
72       * @throws IOException if no configuration was specified
73       * @throws SAXException if an error occurs during parsing
74       */
75      @Override
76      public void parse(final String systemId) throws IOException, SAXException {
77          parseConfiguration();
78      }
79  
80      /**
81       * Parses the actual configuration object. The passed input source will be ignored.
82       *
83       * @param input the input source (ignored)
84       * @throws IOException if no configuration was specified
85       * @throws SAXException if an error occurs during parsing
86       */
87      @Override
88      public void parse(final InputSource input) throws IOException, SAXException {
89          parseConfiguration();
90      }
91  
92      /**
93       * Dummy implementation of the interface method.
94       *
95       * @param name the name of the feature
96       * @return always <b>false</b> (no features are supported)
97       */
98      @Override
99      public boolean getFeature(final String name) {
100         return false;
101     }
102 
103     /**
104      * Dummy implementation of the interface method.
105      *
106      * @param name the name of the feature to be set
107      * @param value the value of the feature
108      */
109     @Override
110     public void setFeature(final String name, final boolean value) {
111     }
112 
113     /**
114      * Gets the actually set content handler.
115      *
116      * @return the content handler
117      */
118     @Override
119     public ContentHandler getContentHandler() {
120         return contentHandler;
121     }
122 
123     /**
124      * Sets the content handler. The object specified here will receive SAX events during parsing.
125      *
126      * @param handler the content handler
127      */
128     @Override
129     public void setContentHandler(final ContentHandler handler) {
130         contentHandler = handler;
131     }
132 
133     /**
134      * Gets the DTD handler. This class does not support DTD handlers, so this method always returns <b>null</b>.
135      *
136      * @return the DTD handler
137      */
138     @Override
139     public DTDHandler getDTDHandler() {
140         return null;
141     }
142 
143     /**
144      * Sets the DTD handler. The passed value is ignored.
145      *
146      * @param handler the handler to be set
147      */
148     @Override
149     public void setDTDHandler(final DTDHandler handler) {
150     }
151 
152     /**
153      * Gets the entity resolver. This class does not support an entity resolver, so this method always returns
154      * <b>null</b>.
155      *
156      * @return the entity resolver
157      */
158     @Override
159     public EntityResolver getEntityResolver() {
160         return null;
161     }
162 
163     /**
164      * Sets the entity resolver. The passed value is ignored.
165      *
166      * @param resolver the entity resolver
167      */
168     @Override
169     public void setEntityResolver(final EntityResolver resolver) {
170     }
171 
172     /**
173      * Gets the error handler. This class does not support an error handler, so this method always returns <b>null</b>.
174      *
175      * @return the error handler
176      */
177     @Override
178     public ErrorHandler getErrorHandler() {
179         return null;
180     }
181 
182     /**
183      * Sets the error handler. The passed value is ignored.
184      *
185      * @param handler the error handler
186      */
187     @Override
188     public void setErrorHandler(final ErrorHandler handler) {
189     }
190 
191     /**
192      * Dummy implementation of the interface method. No properties are supported, so this method always returns <b>null</b>.
193      *
194      * @param name the name of the requested property
195      * @return the property value
196      */
197     @Override
198     public Object getProperty(final String name) {
199         return null;
200     }
201 
202     /**
203      * Dummy implementation of the interface method. No properties are supported, so a call of this method just has no
204      * effect.
205      *
206      * @param name the property name
207      * @param value the property value
208      */
209     @Override
210     public void setProperty(final String name, final Object value) {
211     }
212 
213     /**
214      * Gets the name to be used for the root element.
215      *
216      * @return the name for the root element
217      */
218     public String getRootName() {
219         return rootName;
220     }
221 
222     /**
223      * Sets the name for the root element.
224      *
225      * @param string the name for the root element.
226      */
227     public void setRootName(final String string) {
228         rootName = string;
229     }
230 
231     /**
232      * Fires a SAX element start event.
233      *
234      * @param name the name of the actual element
235      * @param attribs the attributes of this element (can be <b>null</b>)
236      */
237     protected void fireElementStart(final String name, final Attributes attribs) {
238         if (getException() == null) {
239             try {
240                 final Attributes at = attribs == null ? EMPTY_ATTRS : attribs;
241                 getContentHandler().startElement(NS_URI, name, name, at);
242             } catch (final SAXException ex) {
243                 exception = ex;
244             }
245         }
246     }
247 
248     /**
249      * Fires a SAX element end event.
250      *
251      * @param name the name of the affected element
252      */
253     protected void fireElementEnd(final String name) {
254         if (getException() == null) {
255             try {
256                 getContentHandler().endElement(NS_URI, name, name);
257             } catch (final SAXException ex) {
258                 exception = ex;
259             }
260         }
261     }
262 
263     /**
264      * Fires a SAX characters event.
265      *
266      * @param text the text
267      */
268     protected void fireCharacters(final String text) {
269         if (getException() == null) {
270             try {
271                 final char[] ch = text.toCharArray();
272                 getContentHandler().characters(ch, 0, ch.length);
273             } catch (final SAXException ex) {
274                 exception = ex;
275             }
276         }
277     }
278 
279     /**
280      * Gets a reference to an exception that occurred during parsing.
281      *
282      * @return a SAXExcpetion or <b>null</b> if none occurred
283      */
284     public SAXException getException() {
285         return exception;
286     }
287 
288     /**
289      * Parses the configuration object and generates SAX events. This is the main processing method.
290      *
291      * @throws IOException if no configuration has been specified
292      * @throws SAXException if an error occurs during parsing
293      */
294     protected void parseConfiguration() throws IOException, SAXException {
295         if (getParsedConfiguration() == null) {
296             throw new IOException("No configuration specified!");
297         }
298 
299         if (getContentHandler() != null) {
300             exception = null;
301             getContentHandler().startDocument();
302             processKeys();
303             if (getException() != null) {
304                 throw getException();
305             }
306             getContentHandler().endDocument();
307         }
308     }
309 
310     /**
311      * Gets a reference to the configuration that is parsed by this object.
312      *
313      * @return the parsed configuration
314      */
315     public abstract Configuration getParsedConfiguration();
316 
317     /**
318      * Processes all keys stored in the actual configuration. This method is called by {@code parseConfiguration()} to start
319      * the main parsing process. {@code parseConfiguration()} calls the content handler's {@code startDocument()} and
320      * {@code endElement()} methods and cares for exception handling. The remaining actions are left to this method that
321      * must be implemented in a concrete sub class.
322      *
323      * @throws IOException if an IO error occurs
324      * @throws SAXException if a SAX error occurs
325      */
326     protected abstract void processKeys() throws IOException, SAXException;
327 }