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