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