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