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