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 * @version $Id: HierarchicalConfigurationXMLReader.java 1624601 2014-09-12 18:04:36Z oheger $ 045 * @param <T> the type of the nodes supported by this reader 046 */ 047public class HierarchicalConfigurationXMLReader<T> extends 048 ConfigurationXMLReader 049{ 050 /** Stores the configuration object to be parsed. */ 051 private HierarchicalConfiguration<T> configuration; 052 053 /** 054 * Creates a new instance of {@code HierarchicalConfigurationXMLReader}. 055 */ 056 public HierarchicalConfigurationXMLReader() 057 { 058 super(); 059 } 060 061 /** 062 * Creates a new instance of {@code HierarchicalConfigurationXMLReader} and 063 * sets the configuration to be parsed. 064 * 065 * @param config the configuration object 066 */ 067 public HierarchicalConfigurationXMLReader( 068 HierarchicalConfiguration<T> config) 069 { 070 this(); 071 setConfiguration(config); 072 } 073 074 /** 075 * Returns the configuration object to be parsed. 076 * 077 * @return the configuration object to be parsed 078 */ 079 public HierarchicalConfiguration<T> getConfiguration() 080 { 081 return configuration; 082 } 083 084 /** 085 * Sets the configuration object to be parsed. 086 * 087 * @param config the configuration object to be parsed 088 */ 089 public void setConfiguration(HierarchicalConfiguration<T> config) 090 { 091 configuration = config; 092 } 093 094 /** 095 * Returns the configuration object to be processed. 096 * 097 * @return the actual configuration object 098 */ 099 @Override 100 public Configuration getParsedConfiguration() 101 { 102 return getConfiguration(); 103 } 104 105 /** 106 * Processes the actual configuration object to generate SAX parsing events. 107 */ 108 @Override 109 protected void processKeys() 110 { 111 NodeHandler<T> nodeHandler = 112 getConfiguration().getNodeModel().getNodeHandler(); 113 NodeTreeWalker.INSTANCE.walkDFS(nodeHandler.getRootNode(), 114 new SAXVisitor(), nodeHandler); 115 } 116 117 /** 118 * A specialized visitor class for generating SAX events for a hierarchical 119 * node structure. 120 */ 121 private class SAXVisitor extends ConfigurationNodeVisitorAdapter<T> 122 { 123 /** Constant for the attribute type. */ 124 private static final String ATTR_TYPE = "CDATA"; 125 126 /** 127 * Visits the specified node after its children have been processed. 128 * 129 * @param node the actual node 130 * @param handler the node handler 131 */ 132 @Override 133 public void visitAfterChildren(T node, NodeHandler<T> handler) 134 { 135 fireElementEnd(nodeName(node, handler)); 136 } 137 138 /** 139 * Visits the specified node. 140 * 141 * @param node the actual node 142 * @param handler the node handler 143 */ 144 @Override 145 public void visitBeforeChildren(T node, NodeHandler<T> handler) 146 { 147 fireElementStart(nodeName(node, handler), 148 fetchAttributes(node, handler)); 149 150 Object value = handler.getValue(node); 151 if (value != null) 152 { 153 fireCharacters(value.toString()); 154 } 155 } 156 157 /** 158 * Checks if iteration should be terminated. This implementation stops 159 * iteration after an exception has occurred. 160 * 161 * @return a flag if iteration should be stopped 162 */ 163 @Override 164 public boolean terminate() 165 { 166 return getException() != null; 167 } 168 169 /** 170 * Returns an object with all attributes for the specified node. 171 * 172 * @param node the current node 173 * @param handler the node handler 174 * @return an object with all attributes of this node 175 */ 176 protected Attributes fetchAttributes(T node, NodeHandler<T> handler) 177 { 178 AttributesImpl attrs = new AttributesImpl(); 179 180 for (String attr : handler.getAttributes(node)) 181 { 182 Object value = handler.getAttributeValue(node, attr); 183 if (value != null) 184 { 185 attrs.addAttribute(NS_URI, attr, attr, ATTR_TYPE, 186 value.toString()); 187 } 188 } 189 190 return attrs; 191 } 192 193 /** 194 * Helper method for determining the name of a node. If a node has no 195 * name (which is true for the root node), the specified default name 196 * will be used. 197 * 198 * @param node the node to be checked 199 * @param handler the node handler 200 * @return the name for this node 201 */ 202 private String nodeName(T node, NodeHandler<T> handler) 203 { 204 String nodeName = handler.nodeName(node); 205 return (nodeName == null) ? getRootName() : nodeName; 206 } 207 } 208}