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