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
018package org.apache.commons.configuration2;
019
020import java.io.IOException;
021
022import org.xml.sax.Attributes;
023import org.xml.sax.ContentHandler;
024import org.xml.sax.DTDHandler;
025import org.xml.sax.EntityResolver;
026import org.xml.sax.ErrorHandler;
027import org.xml.sax.InputSource;
028import org.xml.sax.SAXException;
029import org.xml.sax.XMLReader;
030import org.xml.sax.helpers.AttributesImpl;
031
032/**
033 * <p>A base class for &quot;faked&quot; {@code XMLReader} classes
034 * that transform a configuration object in a set of SAX parsing events.</p>
035 * <p>This class provides dummy implementations for most of the methods
036 * defined in the {@code XMLReader} interface that are not used for this
037 * special purpose. There will be concrete sub classes that process specific
038 * configuration classes.</p>
039 *
040 */
041public abstract class ConfigurationXMLReader implements XMLReader
042{
043    /** Constant for the namespace URI.*/
044    protected static final String NS_URI = "";
045
046    /** Constant for the default name of the root element.*/
047    private static final String DEFAULT_ROOT_NAME = "config";
048
049    /** An empty attributes object.*/
050    private static final Attributes EMPTY_ATTRS = new AttributesImpl();
051
052    /** Stores the content handler.*/
053    private ContentHandler contentHandler;
054
055    /** Stores an exception that occurred during parsing.*/
056    private SAXException exception;
057
058    /** Stores the name for the root element.*/
059    private String rootName;
060
061    /**
062     * Creates a new instance of {@code ConfigurationXMLReader}.
063     */
064    protected ConfigurationXMLReader()
065    {
066        super();
067        rootName = DEFAULT_ROOT_NAME;
068    }
069
070    /**
071     * Parses the current configuration object. The passed system ID will be
072     * ignored.
073     *
074     * @param systemId the system ID (ignored)
075     * @throws IOException if no configuration was specified
076     * @throws SAXException if an error occurs during parsing
077     */
078    @Override
079    public void parse(final String systemId) throws IOException, SAXException
080    {
081        parseConfiguration();
082    }
083
084    /**
085     * Parses the actual configuration object. The passed input source will be
086     * ignored.
087     *
088     * @param input the input source (ignored)
089     * @throws IOException if no configuration was specified
090     * @throws SAXException if an error occurs during parsing
091     */
092    @Override
093    public void parse(final InputSource input) throws IOException, SAXException
094    {
095        parseConfiguration();
096    }
097
098    /**
099     * Dummy implementation of the interface method.
100     *
101     * @param name the name of the feature
102     * @return always <b>false</b> (no features are supported)
103     */
104    @Override
105    public boolean getFeature(final String name)
106    {
107        return false;
108    }
109
110    /**
111     * Dummy implementation of the interface method.
112     *
113     * @param name the name of the feature to be set
114     * @param value the value of the feature
115     */
116    @Override
117    public void setFeature(final String name, final boolean value)
118    {
119    }
120
121    /**
122     * Returns the actually set content handler.
123     *
124     * @return the content handler
125     */
126    @Override
127    public ContentHandler getContentHandler()
128    {
129        return contentHandler;
130    }
131
132    /**
133     * Sets the content handler. The object specified here will receive SAX
134     * events during parsing.
135     *
136     * @param handler the content handler
137     */
138    @Override
139    public void setContentHandler(final ContentHandler handler)
140    {
141        contentHandler = handler;
142    }
143
144    /**
145     * Returns the DTD handler. This class does not support DTD handlers,
146     * so this method always returns <b>null</b>.
147     *
148     * @return the DTD handler
149     */
150    @Override
151    public DTDHandler getDTDHandler()
152    {
153        return null;
154    }
155
156    /**
157     * Sets the DTD handler. The passed value is ignored.
158     *
159     * @param handler the handler to be set
160     */
161    @Override
162    public void setDTDHandler(final DTDHandler handler)
163    {
164    }
165
166    /**
167     * Returns the entity resolver. This class does not support an entity
168     * resolver, so this method always returns <b>null</b>.
169     *
170     * @return the entity resolver
171     */
172    @Override
173    public EntityResolver getEntityResolver()
174    {
175        return null;
176    }
177
178    /**
179     * Sets the entity resolver. The passed value is ignored.
180     *
181     * @param resolver the entity resolver
182     */
183    @Override
184    public void setEntityResolver(final EntityResolver resolver)
185    {
186    }
187
188    /**
189     * Returns the error handler. This class does not support an error handler,
190     * so this method always returns <b>null</b>.
191     *
192     * @return the error handler
193     */
194    @Override
195    public ErrorHandler getErrorHandler()
196    {
197        return null;
198    }
199
200    /**
201     * Sets the error handler. The passed value is ignored.
202     *
203     * @param handler the error handler
204     */
205    @Override
206    public void setErrorHandler(final ErrorHandler handler)
207    {
208    }
209
210    /**
211     * Dummy implementation of the interface method. No properties are
212     * supported, so this method always returns <b>null</b>.
213     *
214     * @param name the name of the requested property
215     * @return the property value
216     */
217    @Override
218    public Object getProperty(final String name)
219    {
220        return null;
221    }
222
223    /**
224     * Dummy implementation of the interface method. No properties are
225     * supported, so a call of this method just has no effect.
226     *
227     * @param name the property name
228     * @param value the property value
229     */
230    @Override
231    public void setProperty(final String name, final Object value)
232    {
233    }
234
235    /**
236     * Returns the name to be used for the root element.
237     *
238     * @return the name for the root element
239     */
240    public String getRootName()
241    {
242        return rootName;
243    }
244
245    /**
246     * Sets the name for the root element.
247     *
248     * @param string the name for the root element.
249     */
250    public void setRootName(final String string)
251    {
252        rootName = string;
253    }
254
255    /**
256     * Fires a SAX element start event.
257     *
258     * @param name the name of the actual element
259     * @param attribs the attributes of this element (can be <b>null</b>)
260     */
261    protected void fireElementStart(final String name, final Attributes attribs)
262    {
263        if (getException() == null)
264        {
265            try
266            {
267                final Attributes at = attribs == null ? EMPTY_ATTRS : attribs;
268                getContentHandler().startElement(NS_URI, name, name, at);
269            }
270            catch (final SAXException ex)
271            {
272                exception = ex;
273            }
274        }
275    }
276
277    /**
278     * Fires a SAX element end event.
279     *
280     * @param name the name of the affected element
281     */
282    protected void fireElementEnd(final String name)
283    {
284        if (getException() == null)
285        {
286            try
287            {
288                getContentHandler().endElement(NS_URI, name, name);
289            }
290            catch (final SAXException ex)
291            {
292                exception = ex;
293            }
294        }
295    }
296
297    /**
298     * Fires a SAX characters event.
299     *
300     * @param text the text
301     */
302    protected void fireCharacters(final String text)
303    {
304        if (getException() == null)
305        {
306            try
307            {
308                final char[] ch = text.toCharArray();
309                getContentHandler().characters(ch, 0, ch.length);
310            }
311            catch (final SAXException ex)
312            {
313                exception = ex;
314            }
315        }
316    }
317
318    /**
319     * Returns a reference to an exception that occurred during parsing.
320     *
321     * @return a SAXExcpetion or <b>null</b> if none occurred
322     */
323    public SAXException getException()
324    {
325        return exception;
326    }
327
328    /**
329     * Parses the configuration object and generates SAX events. This is the
330     * main processing method.
331     *
332     * @throws IOException if no configuration has been specified
333     * @throws SAXException if an error occurs during parsing
334     */
335    protected void parseConfiguration() throws IOException, SAXException
336    {
337        if (getParsedConfiguration() == null)
338        {
339            throw new IOException("No configuration specified!");
340        }
341
342        if (getContentHandler() != null)
343        {
344            exception = null;
345            getContentHandler().startDocument();
346            processKeys();
347            if (getException() != null)
348            {
349                throw getException();
350            }
351            getContentHandler().endDocument();
352        }
353    }
354
355    /**
356     * Returns a reference to the configuration that is parsed by this object.
357     *
358     * @return the parsed configuration
359     */
360    public abstract Configuration getParsedConfiguration();
361
362    /**
363     * Processes all keys stored in the actual configuration. This method is
364     * called by {@code parseConfiguration()} to start the main parsing
365     * process. {@code parseConfiguration()} calls the content handler's
366     * {@code startDocument()} and {@code endElement()} methods
367     * and cares for exception handling. The remaining actions are left to this
368     * method that must be implemented in a concrete sub class.
369     *
370     * @throws IOException if an IO error occurs
371     * @throws SAXException if a SAX error occurs
372     */
373    protected abstract void processKeys() throws IOException, SAXException;
374}