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