    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 org.apache.commons.configuration.HierarchicalConfiguration.Node;
    021    import org.apache.commons.configuration.tree.ConfigurationNode;
    022    import org.xml.sax.Attributes;
    023    import org.xml.sax.helpers.AttributesImpl;
    024
    025    /**
    026     * <p>A specialized SAX2 XML parser that "parses" hierarchical
    027     * configuration objects.</p>
    028     * <p>This class mimics to be a SAX conform XML parser. Instead of parsing
    029     * XML documents it processes a {@code Configuration} object and
    030     * generates SAX events for the single properties defined there. This enables
    031     * the whole world of XML processing for configuration objects.</p>
    032     * <p>The {@code HierarchicalConfiguration} object to be parsed can be
    033     * specified using a constructor or the {@code setConfiguration()} method.
    034     * This object will be processed by the {@code parse()} methods. Note
    035     * that these methods ignore their argument.</p>
    036     *
    037     * @author <a
    038     * href="http://commons.apache.org/configuration/team-list.html">Commons Configuration team</a>
    039     * @version $Id: HierarchicalConfigurationXMLReader.java 1209998 2011-12-03 20:31:16Z oheger $
    040     */
    041    public class HierarchicalConfigurationXMLReader extends ConfigurationXMLReader
    042    {
    043        /** Stores the configuration object to be parsed.*/
    044        private HierarchicalConfiguration configuration;
    045
    046        /**
    047         * Creates a new instance of {@code HierarchicalConfigurationXMLReader}.
    048         */
    049        public HierarchicalConfigurationXMLReader()
    050        {
    051            super();
    052        }
    053
    054        /**
    055         * Creates a new instance of {@code HierarchicalConfigurationXMLReader} and
    056         * sets the configuration to be parsed.
    057         *
    058         * @param config the configuration object
    059         */
    060        public HierarchicalConfigurationXMLReader(HierarchicalConfiguration config)
    061        {
    062            this();
    063            setConfiguration(config);
    064        }
    065
    066        /**
    067         * Returns the configuration object to be parsed.
    068         *
    069         * @return the configuration object to be parsed
    070         */
    071        public HierarchicalConfiguration getConfiguration()
    072        {
    073            return configuration;
    074        }
    075
    076        /**
    077         * Sets the configuration object to be parsed.
    078         *
    079         * @param config the configuration object to be parsed
    080         */
    081        public void setConfiguration(HierarchicalConfiguration config)
    082        {
    083            configuration = config;
    084        }
    085
    086        /**
    087         * Returns the configuration object to be processed.
    088         *
    089         * @return the actual configuration object
    090         */
    091        @Override
    092        public Configuration getParsedConfiguration()
    093        {
    094            return getConfiguration();
    095        }
    096
    097        /**
    098         * Processes the actual configuration object to generate SAX parsing events.
    099         */
    100        @Override
    101        protected void processKeys()
    102        {
    103            getConfiguration().getRoot().visit(new SAXVisitor(), null);
    104        }
    105
    106        /**
    107         * A specialized visitor class for generating SAX events for a
    108         * hierarchical node structure.
    109         *
    110         */
    111        class SAXVisitor extends HierarchicalConfiguration.NodeVisitor
    112        {
    113            /** Constant for the attribute type.*/
    114            private static final String ATTR_TYPE = "CDATA";
    115
    116            /**
    117             * Visits the specified node after its children have been processed.
    118             *
    119             * @param node the actual node
    120             * @param key the key of this node
    121             */
    122            @Override
    123            public void visitAfterChildren(Node node, ConfigurationKey key)
    124            {
    125                if (!isAttributeNode(node))
    126                {
    127                    fireElementEnd(nodeName(node));
    128                }
    129            }
    130
    131            /**
    132             * Visits the specified node.
    133             *
    134             * @param node the actual node
    135             * @param key the key of this node
    136             */
    137            @Override
    138            public void visitBeforeChildren(Node node, ConfigurationKey key)
    139            {
    140                if (!isAttributeNode(node))
    141                {
    142                    fireElementStart(nodeName(node), fetchAttributes(node));
    143
    144                    if (node.getValue() != null)
    145                    {
    146                        fireCharacters(node.getValue().toString());
    147                    }
    148                }
    149            }
    150
    151            /**
    152             * Checks if iteration should be terminated. This implementation stops
    153             * iteration after an exception has occurred.
    154             *
    155             * @return a flag if iteration should be stopped
    156             */
    157            @Override
    158            public boolean terminate()
    159            {
    160                return getException() != null;
    161            }
    162
    163            /**
    164             * Returns an object with all attributes for the specified node.
    165             *
    166             * @param node the actual node
    167             * @return an object with all attributes of this node
    168             */
    169            protected Attributes fetchAttributes(Node node)
    170            {
    171                AttributesImpl attrs = new AttributesImpl();
    172
    173                for (ConfigurationNode child : node.getAttributes())
    174                {
    175                    if (child.getValue() != null)
    176                    {
    177                        String attr = child.getName();
    178                        attrs.addAttribute(NS_URI, attr, attr, ATTR_TYPE, child.getValue().toString());
    179                    }
    180                }
    181
    182                return attrs;
    183            }
    184
    185            /**
    186             * Helper method for determining the name of a node. If a node has no
    187             * name (which is true for the root node), the specified default name
    188             * will be used.
    189             *
    190             * @param node the node to be checked
    191             * @return the name for this node
    192             */
    193            private String nodeName(Node node)
    194            {
    195                return (node.getName() == null) ? getRootName() : node.getName();
    196            }
    197
    198            /**
    199             * Checks if the specified node is an attribute node. In the node
    200             * hierarchy attributes are stored as normal child nodes, but with
    201             * special names.
    202             *
    203             * @param node the node to be checked
    204             * @return a flag if this is an attribute node
    205             */
    206            private boolean isAttributeNode(Node node)
    207            {
    208                return node.isAttribute();
    209            }
    210        }
    211    }