    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}