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 }