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