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 }