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