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