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.configuration2; 019 020import org.apache.commons.configuration2.tree.ConfigurationNodeVisitorAdapter; 021import org.apache.commons.configuration2.tree.NodeHandler; 022import org.apache.commons.configuration2.tree.NodeTreeWalker; 023import org.xml.sax.Attributes; 024import org.xml.sax.helpers.AttributesImpl; 025 026/** 027 * <p> 028 * A specialized SAX2 XML parser that "parses" hierarchical configuration 029 * objects. 030 * </p> 031 * <p> 032 * This class mimics to be a SAX conform XML parser. Instead of parsing XML 033 * documents it processes a {@code Configuration} object and generates SAX 034 * events for the single properties defined there. This enables the whole world 035 * of XML processing for configuration objects. 036 * </p> 037 * <p> 038 * The {@code HierarchicalConfiguration} object to be parsed can be specified 039 * using a constructor or the {@code setConfiguration()} method. This object 040 * will be processed by the {@code parse()} methods. Note that these methods 041 * ignore their argument. 042 * </p> 043 * 044 * @param <T> the type of the nodes supported by this reader 045 */ 046public class HierarchicalConfigurationXMLReader<T> extends 047 ConfigurationXMLReader 048{ 049 /** Stores the configuration object to be parsed. */ 050 private HierarchicalConfiguration<T> configuration; 051 052 /** 053 * Creates a new instance of {@code HierarchicalConfigurationXMLReader}. 054 */ 055 public HierarchicalConfigurationXMLReader() 056 { 057 super(); 058 } 059 060 /** 061 * Creates a new instance of {@code HierarchicalConfigurationXMLReader} and 062 * sets the configuration to be parsed. 063 * 064 * @param config the configuration object 065 */ 066 public HierarchicalConfigurationXMLReader( 067 final HierarchicalConfiguration<T> config) 068 { 069 this(); 070 setConfiguration(config); 071 } 072 073 /** 074 * Returns the configuration object to be parsed. 075 * 076 * @return the configuration object to be parsed 077 */ 078 public HierarchicalConfiguration<T> getConfiguration() 079 { 080 return configuration; 081 } 082 083 /** 084 * Sets the configuration object to be parsed. 085 * 086 * @param config the configuration object to be parsed 087 */ 088 public void setConfiguration(final HierarchicalConfiguration<T> config) 089 { 090 configuration = config; 091 } 092 093 /** 094 * Returns the configuration object to be processed. 095 * 096 * @return the actual configuration object 097 */ 098 @Override 099 public Configuration getParsedConfiguration() 100 { 101 return getConfiguration(); 102 } 103 104 /** 105 * Processes the actual configuration object to generate SAX parsing events. 106 */ 107 @Override 108 protected void processKeys() 109 { 110 final NodeHandler<T> nodeHandler = 111 getConfiguration().getNodeModel().getNodeHandler(); 112 NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(), 113 new SAXVisitor(), nodeHandler); 114 } 115 116 /** 117 * A specialized visitor class for generating SAX events for a hierarchical 118 * node structure. 119 */ 120 private class SAXVisitor extends ConfigurationNodeVisitorAdapter<T> 121 { 122 /** Constant for the attribute type. */ 123 private static final String ATTR_TYPE = "CDATA"; 124 125 /** 126 * Visits the specified node after its children have been processed. 127 * 128 * @param node the actual node 129 * @param handler the node handler 130 */ 131 @Override 132 public void visitAfterChildren(final T node, final NodeHandler<T> handler) 133 { 134 fireElementEnd(nodeName(node, handler)); 135 } 136 137 /** 138 * Visits the specified node. 139 * 140 * @param node the actual node 141 * @param handler the node handler 142 */ 143 @Override 144 public void visitBeforeChildren(final T node, final NodeHandler<T> handler) 145 { 146 fireElementStart(nodeName(node, handler), 147 fetchAttributes(node, handler)); 148 149 final Object value = handler.getValue(node); 150 if (value != null) 151 { 152 fireCharacters(value.toString()); 153 } 154 } 155 156 /** 157 * Checks if iteration should be terminated. This implementation stops 158 * iteration after an exception has occurred. 159 * 160 * @return a flag if iteration should be stopped 161 */ 162 @Override 163 public boolean terminate() 164 { 165 return getException() != null; 166 } 167 168 /** 169 * Returns an object with all attributes for the specified node. 170 * 171 * @param node the current node 172 * @param handler the node handler 173 * @return an object with all attributes of this node 174 */ 175 protected Attributes fetchAttributes(final T node, final NodeHandler<T> handler) 176 { 177 final AttributesImpl attrs = new AttributesImpl(); 178 179 for (final String attr : handler.getAttributes(node)) 180 { 181 final Object value = handler.getAttributeValue(node, attr); 182 if (value != null) 183 { 184 attrs.addAttribute(NS_URI, attr, attr, ATTR_TYPE, 185 value.toString()); 186 } 187 } 188 189 return attrs; 190 } 191 192 /** 193 * Helper method for determining the name of a node. If a node has no 194 * name (which is true for the root node), the specified default name 195 * will be used. 196 * 197 * @param node the node to be checked 198 * @param handler the node handler 199 * @return the name for this node 200 */ 201 private String nodeName(final T node, final NodeHandler<T> handler) 202 { 203 final String nodeName = handler.nodeName(node); 204 return nodeName == null ? getRootName() : nodeName; 205 } 206 } 207}