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 }