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