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