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.configuration;
019
020import org.apache.commons.configuration.HierarchicalConfiguration.Node;
021import org.apache.commons.configuration.tree.ConfigurationNode;
022import org.xml.sax.Attributes;
023import 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 */
041public 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}