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