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