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