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