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    }