Copied: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/InMemoryConfiguration.java (from r632835, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java) URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/InMemoryConfiguration.java?p2=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/InMemoryConfiguration.java&p1=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java&r1=632835&r2=635081&rev=635081&view=diff ============================================================================== --- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/HierarchicalConfiguration.java (original) +++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/InMemoryConfiguration.java Sat Mar 8 12:42:32 2008 @@ -17,26 +17,20 @@ package org.apache.commons.configuration2; -import java.io.Serializable; -import java.util.ArrayList; import java.util.Collection; -import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; -import java.util.Set; import java.util.Stack; -import org.apache.commons.configuration2.event.ConfigurationEvent; -import org.apache.commons.configuration2.event.ConfigurationListener; +import org.apache.commons.configuration2.expr.ConfigurationNodeHandler; +import org.apache.commons.configuration2.expr.NodeAddData; +import org.apache.commons.configuration2.expr.NodeHandler; +import org.apache.commons.configuration2.expr.NodeList; +import org.apache.commons.configuration2.expr.NodeVisitor; +import org.apache.commons.configuration2.expr.NodeVisitorAdapter; import org.apache.commons.configuration2.tree.ConfigurationNode; -import org.apache.commons.configuration2.tree.ConfigurationNodeVisitor; -import org.apache.commons.configuration2.tree.ConfigurationNodeVisitorAdapter; import org.apache.commons.configuration2.tree.DefaultConfigurationNode; -import org.apache.commons.configuration2.tree.DefaultExpressionEngine; -import org.apache.commons.configuration2.tree.ExpressionEngine; -import org.apache.commons.configuration2.tree.NodeAddData; /** * <p>A specialized configuration class that extends its base class by the @@ -123,66 +117,43 @@ * synchronization has to be performed manually.</p> * * @author Oliver Heger + * @since 2.0 * @version $Id$ */ -public class HierarchicalConfiguration extends AbstractConfiguration implements Serializable, Cloneable +public class InMemoryConfiguration extends AbstractHierarchicalConfiguration<ConfigurationNode> implements Cloneable { /** - * Constant for the clear tree event. - * @since 1.3 - */ - public static final int EVENT_CLEAR_TREE = 10; - - /** * Constant for the add nodes event. - * @since 1.3 */ public static final int EVENT_ADD_NODES = 11; - /** - * Constant for the subnode configuration modified event. - * @since 1.5 - */ - public static final int EVENT_SUBNODE_CHANGED = 12; - - /** - * The serial version UID. - */ - private static final long serialVersionUID = 3373812230395363192L; - - /** Stores the default expression engine to be used for new objects.*/ - private static ExpressionEngine defaultExpressionEngine; - /** Stores the root configuration node.*/ private ConfigurationNode rootNode; - /** Stores the expression engine for this instance.*/ - private transient ExpressionEngine expressionEngine; - /** - * Creates a new instance of <code>HierarchicalConfiguration</code>. + * Creates a new instance of <code>InMemoryConfiguration</code>. */ - public HierarchicalConfiguration() + public InMemoryConfiguration() { + super(new ConfigurationNodeHandler()); setRootNode(new DefaultConfigurationNode()); } /** - * Creates a new instance of <code>HierarchicalConfiguration</code> and + * Creates a new instance of <code>InMemoryConfiguration</code> and * copies all data contained in the specified configuration into the new * one. * * @param c the configuration that is to be copied (if <b>null</b>, this * constructor will behave like the standard constructor) - * @since 1.4 */ - public HierarchicalConfiguration(HierarchicalConfiguration c) + public InMemoryConfiguration(InMemoryConfiguration c) { this(); if (c != null) { CloneVisitor visitor = new CloneVisitor(); - c.getRootNode().visit(visitor); + visit(c.getRootNode(), visitor); setRootNode(visitor.getClone()); } } @@ -191,8 +162,8 @@ * Returns the root node of this hierarchical configuration. * * @return the root node - * @since 1.3 */ + @Override public ConfigurationNode getRootNode() { return rootNode; @@ -202,7 +173,6 @@ * Sets the root node of this hierarchical configuration. * * @param rootNode the root node - * @since 1.3 */ public void setRootNode(ConfigurationNode rootNode) { @@ -214,119 +184,6 @@ } /** - * Returns the default expression engine. - * - * @return the default expression engine - * @since 1.3 - */ - public static synchronized ExpressionEngine getDefaultExpressionEngine() - { - if (defaultExpressionEngine == null) - { - defaultExpressionEngine = new DefaultExpressionEngine(); - } - return defaultExpressionEngine; - } - - /** - * Sets the default expression engine. This expression engine will be used - * if no specific engine was set for an instance. It is shared between all - * hierarchical configuration instances. So modifying its properties will - * impact all instances, for which no specific engine is set. - * - * @param engine the new default expression engine - * @since 1.3 - */ - public static synchronized void setDefaultExpressionEngine(ExpressionEngine engine) - { - if (engine == null) - { - throw new IllegalArgumentException("Default expression engine must not be null!"); - } - defaultExpressionEngine = engine; - } - - /** - * Returns the expression engine used by this configuration. This method - * will never return <b>null</b>; if no specific expression engine was set, - * the default expression engine will be returned. - * - * @return the current expression engine - * @since 1.3 - */ - public ExpressionEngine getExpressionEngine() - { - return (expressionEngine != null) ? expressionEngine : getDefaultExpressionEngine(); - } - - /** - * Sets the expression engine to be used by this configuration. All property - * keys this configuration has to deal with will be interpreted by this - * engine. - * - * @param expressionEngine the new expression engine; can be <b>null</b>, - * then the default expression engine will be used - * @since 1.3 - */ - public void setExpressionEngine(ExpressionEngine expressionEngine) - { - this.expressionEngine = expressionEngine; - } - - /** - * Fetches the specified property. This task is delegated to the associated - * expression engine. - * - * @param key the key to be looked up - * @return the found value - */ - public Object getProperty(String key) - { - List<ConfigurationNode> nodes = fetchNodeList(key); - - if (nodes.size() == 0) - { - return null; - } - else - { - List<Object> list = new ArrayList<Object>(); - for (ConfigurationNode node : nodes) - { - if (node.getValue() != null) - { - list.add(node.getValue()); - } - } - - if (list.size() < 1) - { - return null; - } - else - { - return (list.size() == 1) ? list.get(0) : list; - } - } - } - - /** - * Adds the property with the specified key. This task will be delegated to - * the associated <code>ExpressionEngine</code>, so the passed in key - * must match the requirements of this implementation. - * - * @param key the key of the new property - * @param obj the value of the new property - */ - @Override - protected void addPropertyDirect(String key, Object obj) - { - NodeAddData data = getExpressionEngine().prepareAdd(getRootNode(), key); - ConfigurationNode node = processNodeAddData(data); - node.setValue(obj); - } - - /** * Adds a collection of nodes at the specified position of the configuration * tree. This method works similar to <code>addProperty()</code>, but * instead of a single property a whole collection of nodes can be added - @@ -346,6 +203,7 @@ * then they are added to the root node * @param nodes a collection with the <code>Node</code> objects to be * added + * @throws IllegalArgumentException if the key specifies an attribute */ public void addNodes(String key, Collection<? extends ConfigurationNode> nodes) { @@ -356,28 +214,32 @@ fireEvent(EVENT_ADD_NODES, key, nodes, true); ConfigurationNode parent; - List<ConfigurationNode> target = fetchNodeList(key); + NodeList<ConfigurationNode> target = fetchNodeList(key); if (target.size() == 1) { // existing unique key - parent = target.get(0); + parent = target.getNode(0); } else { // otherwise perform an add operation - parent = processNodeAddData(getExpressionEngine().prepareAdd(getRootNode(), key)); - } - - if (parent.isAttribute()) - { - throw new IllegalArgumentException("Cannot add nodes to an attribute node!"); + NodeAddData<ConfigurationNode> addData = getExpressionEngine() + .prepareAdd(getRootNode(), key, getNodeHandler()); + if (addData.isAttribute()) + { + throw new IllegalArgumentException( + "Cannot add nodes to an attribute node!"); + } + parent = processNodeAddData(addData, null); } // a visitor to ensure that the nodes' references are cleared; this is // necessary if the nodes are moved from another configuration - ConfigurationNodeVisitor clearRefVisitor = new ConfigurationNodeVisitorAdapter() + NodeVisitor<ConfigurationNode> clearRefVisitor = new NodeVisitorAdapter<ConfigurationNode>() { - public void visitBeforeChildren(ConfigurationNode node) + @Override + public void visitBeforeChildren(ConfigurationNode node, + NodeHandler<ConfigurationNode> handler) { node.setReference(null); } @@ -393,26 +255,15 @@ { parent.addChild(child); } - child.visit(clearRefVisitor); + visit(child, clearRefVisitor); } fireEvent(EVENT_ADD_NODES, key, nodes, false); } /** - * Checks if this configuration is empty. Empty means that there are no keys - * with any values, though there can be some (empty) nodes. - * - * @return a flag if this configuration is empty - */ - public boolean isEmpty() - { - return !nodeDefined(getRootNode()); - } - - /** * Creates a new <code>Configuration</code> object containing all keys - * that start with the specified prefix. This implementation will return a - * <code>HierarchicalConfiguration</code> object so that the structure of + * that start with the specified prefix. This implementation will return an + * <code>InMemoryConfiguration</code> object so that the structure of * the keys will be saved. The nodes selected by the prefix (it is possible * that multiple nodes are selected) are mapped to the root node of the * returned configuration, i.e. their children and attributes will become @@ -431,16 +282,17 @@ @SuppressWarnings("serial") public Configuration subset(String prefix) { - Collection<ConfigurationNode> nodes = fetchNodeList(prefix); - if (nodes.isEmpty()) + NodeList<ConfigurationNode> nodes = fetchNodeList(prefix); + if (nodes.size() < 1) { - return new HierarchicalConfiguration(); + return new InMemoryConfiguration(); } - final HierarchicalConfiguration parent = this; - HierarchicalConfiguration result = new HierarchicalConfiguration() + final InMemoryConfiguration parent = this; + InMemoryConfiguration result = new InMemoryConfiguration() { // Override interpolate to always interpolate on the parent + @Override protected Object interpolate(Object value) { return parent.interpolate(value); @@ -451,22 +303,34 @@ // Initialize the new root node Object value = null; int valueCount = 0; - for (ConfigurationNode nd : nodes) + for (int index = 0; index < nodes.size(); index++) { - if (nd.getValue() != null) + Object v = nodes.getValue(index, getNodeHandler()); + if (v != null) { - value = nd.getValue(); + value = v; valueCount++; } - nd.visit(visitor); - for (ConfigurationNode child : visitor.getClone().getChildren()) + if (nodes.isAttribute(index)) { - result.getRootNode().addChild(child); + getNodeHandler().setAttributeValue(result.getRootNode(), + nodes.getName(index, getNodeHandler()), + nodes.getValue(index, getNodeHandler())); } - for (ConfigurationNode attr : visitor.getClone().getAttributes()) + + else { - result.getRootNode().addAttribute(attr); + visit(nodes.getNode(index), visitor); + for (ConfigurationNode child : visitor.getClone().getChildren()) + { + result.getRootNode().addChild(child); + } + for (ConfigurationNode attr : visitor.getClone() + .getAttributes()) + { + result.getRootNode().addAttribute(attr); + } } } @@ -479,356 +343,22 @@ } /** - * <p> - * Returns a hierarchical subnode configuration object that wraps the - * configuration node specified by the given key. This method provides an - * easy means of accessing sub trees of a hierarchical configuration. In the - * returned configuration the sub tree can directly be accessed, it becomes - * the root node of this configuration. Because of this the passed in key - * must select exactly one configuration node; otherwise an - * <code>IllegalArgumentException</code> will be thrown. - * </p> - * <p> - * The difference between this method and the - * <code>[EMAIL PROTECTED] #subset(String)}</code> method is that - * <code>subset()</code> supports arbitrary subsets of configuration nodes - * while <code>configurationAt()</code> only returns a single sub tree. - * Please refer to the documentation of the - * <code>SubnodeConfiguration</code> class to obtain further information - * about subnode configurations and when they should be used. - * </p> - * <p> - * With the <code>supportUpdate</code> flag the behavior of the returned - * <code>SubnodeConfiguration</code> regarding updates of its parent - * configuration can be determined. A subnode configuration operates on the - * same nodes as its parent, so changes at one configuration are normally - * directly visible for the other configuration. There are however changes - * of the parent configuration, which are not recognized by the subnode - * configuration per default. An example for this is a reload operation (for - * file-based configurations): Here the complete node set of the parent - * configuration is replaced, but the subnode configuration still references - * the old nodes. If such changes should be detected by the subnode - * configuration, the <code>supportUpdates</code> flag must be set to - * <b>true</b>. This causes the subnode configuration to reevaluate the key - * used for its creation each time it is accessed. This guarantees that the - * subnode configuration always stays in sync with its key, even if the - * parent configuration's data significantly changes. If such a change - * makes the key invalid - because it now no longer points to exactly one - * node -, the subnode configuration is not reconstructed, but keeps its - * old data. It is then quasi detached from its parent. - * </p> - * - * @param key the key that selects the sub tree - * @param supportUpdates a flag whether the returned subnode configuration - * should be able to handle updates of its parent - * @return a hierarchical configuration that contains this sub tree - * @see SubnodeConfiguration - * @since 1.5 - */ - public SubnodeConfiguration configurationAt(String key, boolean supportUpdates) - { - List<ConfigurationNode> nodes = fetchNodeList(key); - if (nodes.size() != 1) - { - throw new IllegalArgumentException("Passed in key must select exactly one node: " + key); - } - return supportUpdates ? createSubnodeConfiguration(nodes.get(0), key) : createSubnodeConfiguration(nodes.get(0)); - } - - /** - * Returns a hierarchical subnode configuration for the node specified by - * the given key. This is a short form for <code>configurationAt(key, - * <b>false</b>)</code>. - * - * @param key the key that selects the sub tree - * @return a hierarchical configuration that contains this sub tree - * @see SubnodeConfiguration - * @since 1.3 - */ - public SubnodeConfiguration configurationAt(String key) - { - return configurationAt(key, false); - } - - /** - * Returns a list of sub configurations for all configuration nodes selected - * by the given key. This method will evaluate the passed in key (using the - * current <code>ExpressionEngine</code>) and then create a subnode - * configuration for each returned node (like - * <code>[EMAIL PROTECTED] #configurationAt(String)}</code>}). This is especially - * useful when dealing with list-like structures. As an example consider the - * configuration that contains data about database tables and their fields. - * If you need access to all fields of a certain table, you can simply do - * - * <pre> - * List fields = config.configurationsAt("tables.table(0).fields.field"); - * for(Iterator it = fields.iterator(); it.hasNext();) - * { - * HierarchicalConfiguration sub = (HierarchicalConfiguration) it.next(); - * // now the children and attributes of the field node can be - * // directly accessed - * String fieldName = sub.getString("name"); - * String fieldType = sub.getString("type"); - * ... - * </pre> - * - * @param key the key for selecting the desired nodes - * @return a list with hierarchical configuration objects; each - * configuration represents one of the nodes selected by the passed in key - * @since 1.3 - */ - public List<HierarchicalConfiguration> configurationsAt(String key) - { - List<ConfigurationNode> nodes = fetchNodeList(key); - List<HierarchicalConfiguration> configs = new ArrayList<HierarchicalConfiguration>(nodes.size()); - for (ConfigurationNode node : nodes) - { - configs.add(createSubnodeConfiguration(node)); - } - return configs; - } - - /** - * Creates a subnode configuration for the specified node. This method is - * called by <code>configurationAt()</code> and - * <code>configurationsAt()</code>. - * - * @param node the node, for which a subnode configuration is to be created - * @return the configuration for the given node - * @since 1.3 - */ - protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node) - { - SubnodeConfiguration result = new SubnodeConfiguration(this, node); - registerSubnodeConfiguration(result); - return result; - } - - /** - * Creates a new subnode configuration for the specified node and sets its - * construction key. A subnode configuration created this way will be aware - * of structural changes of its parent. - * - * @param node the node, for which a subnode configuration is to be created - * @param subnodeKey the key used to construct the configuration - * @return the configuration for the given node - * @since 1.5 - */ - protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node, String subnodeKey) - { - SubnodeConfiguration result = createSubnodeConfiguration(node); - result.setSubnodeKey(subnodeKey); - return result; - } - - /** - * This method is always called when a subnode configuration created from - * this configuration has been modified. This implementation transforms the - * received event into an event of type <code>EVENT_SUBNODE_CHANGED</code> - * and notifies the registered listeners. - * - * @param event the event describing the change - * @since 1.5 - */ - protected void subnodeConfigurationChanged(ConfigurationEvent event) - { - fireEvent(EVENT_SUBNODE_CHANGED, null, event, event.isBeforeUpdate()); - } - - /** - * Registers this instance at the given subnode configuration. This - * implementation will register a change listener, so that modifications of - * the subnode configuration can be tracked. - * - * @param config the subnode configuration - * @since 1.5 - */ - void registerSubnodeConfiguration(SubnodeConfiguration config) - { - config.addConfigurationListener(new ConfigurationListener() - { - public void configurationChanged(ConfigurationEvent event) - { - subnodeConfigurationChanged(event); - } - }); - } - - /** - * Checks if the specified key is contained in this configuration. Note that - * for this configuration the term "contained" means that the key - * has an associated value. If there is a node for this key that has no - * value but children (either defined or undefined), this method will still - * return <b>false </b>. - * - * @param key the key to be checked - * @return a flag if this key is contained in this configuration - */ - public boolean containsKey(String key) - { - return getProperty(key) != null; - } - - /** - * Sets the value of the specified property. - * - * @param key the key of the property to set - * @param value the new value of this property - */ - @Override - public void setProperty(String key, Object value) - { - fireEvent(EVENT_SET_PROPERTY, key, value, true); - - // Update the existing nodes for this property - Iterator<ConfigurationNode> itNodes = fetchNodeList(key).iterator(); - Iterator<?> itValues; - if (!isDelimiterParsingDisabled()) - { - itValues = PropertyConverter.toIterator(value, getListDelimiter()); - } - else - { - itValues = Collections.singleton(value).iterator(); - } - - while (itNodes.hasNext() && itValues.hasNext()) - { - itNodes.next().setValue(itValues.next()); - } - - // Add additional nodes if necessary - while (itValues.hasNext()) - { - addPropertyDirect(key, itValues.next()); - } - - // Remove remaining nodes - while (itNodes.hasNext()) - { - clearNode(itNodes.next()); - } - - fireEvent(EVENT_SET_PROPERTY, key, value, false); - } - - /** - * Removes all values of the property with the given name and of keys that - * start with this name. So if there is a property with the key - * "foo" and a property with the key "foo.bar", a call - * of <code>clearTree("foo")</code> would remove both properties. - * - * @param key the key of the property to be removed - */ - public void clearTree(String key) - { - fireEvent(EVENT_CLEAR_TREE, key, null, true); - List<ConfigurationNode> nodes = fetchNodeList(key); - - for (ConfigurationNode node : nodes) - { - removeNode(node); - } - fireEvent(EVENT_CLEAR_TREE, key, nodes, false); - } - - /** - * Removes the property with the given key. Properties with names that start - * with the given key (i.e. properties below the specified key in the - * hierarchy) won't be affected. - * - * @param key the key of the property to be removed - */ - @Override - public void clearProperty(String key) - { - fireEvent(EVENT_CLEAR_PROPERTY, key, null, true); - List<ConfigurationNode> nodes = fetchNodeList(key); - - for (ConfigurationNode node : nodes) - { - clearNode(node); - } - - fireEvent(EVENT_CLEAR_PROPERTY, key, null, false); - } - - /** - * Returns an iterator with all keys defined in this configuration. - * Note that the keys returned by this method will not contain any - * indices. This means that some structure will be lost.</p> - * - * @return an iterator with the defined keys in this configuration - */ - public Iterator<String> getKeys() - { - DefinedKeysVisitor visitor = new DefinedKeysVisitor(); - getRootNode().visit(visitor); - - return visitor.getKeyList().iterator(); - } - - /** - * Returns an iterator with all keys defined in this configuration that - * start with the given prefix. The returned keys will not contain any - * indices. - * - * @param prefix the prefix of the keys to start with - * @return an iterator with the found keys - */ - @Override - public Iterator<String> getKeys(String prefix) - { - DefinedKeysVisitor visitor = new DefinedKeysVisitor(prefix); - List<ConfigurationNode> nodes = fetchNodeList(prefix); - - for (ConfigurationNode node : nodes) - { - for (ConfigurationNode child : node.getChildren()) - { - child.visit(visitor); - } - for (ConfigurationNode attr : node.getAttributes()) - { - attr.visit(visitor); - } - } - - return visitor.getKeyList().iterator(); - } - - /** - * Returns the maximum defined index for the given key. This is useful if - * there are multiple values for this key. They can then be addressed - * separately by specifying indices from 0 to the return value of this - * method. - * - * @param key the key to be checked - * @return the maximum defined index for this key - */ - public int getMaxIndex(String key) - { - return fetchNodeList(key).size() - 1; - } - - /** * Creates a copy of this object. This new configuration object will contain * copies of all nodes in the same structure. Registered event listeners * won't be cloned; so they are not registered at the returned copy. * * @return the copy - * @since 1.2 */ @Override public Object clone() { try { - HierarchicalConfiguration copy = (HierarchicalConfiguration) super.clone(); + InMemoryConfiguration copy = (InMemoryConfiguration) super.clone(); // clone the nodes, too CloneVisitor v = new CloneVisitor(); - getRootNode().visit(v); + visit(getRootNode(), v); copy.setRootNode(v.getClone()); return copy; @@ -848,15 +378,16 @@ * interpolation on the single configuration nodes. * * @return a configuration with all variables interpolated - * @since 1.5 */ @Override public Configuration interpolatedConfiguration() { - HierarchicalConfiguration c = (HierarchicalConfiguration) clone(); - c.getRootNode().visit(new ConfigurationNodeVisitorAdapter() + InMemoryConfiguration c = (InMemoryConfiguration) clone(); + visit(c.getRootNode(), new NodeVisitorAdapter<ConfigurationNode>() { - public void visitAfterChildren(ConfigurationNode node) + @Override + public void visitAfterChildren(ConfigurationNode node, + NodeHandler<ConfigurationNode> handler) { node.setValue(interpolate(node.getValue())); } @@ -865,113 +396,6 @@ } /** - * Helper method for fetching a list of all nodes that are addressed by the - * specified key. - * - * @param key the key - * @return a list with all affected nodes (never <b>null </b>) - */ - protected List<ConfigurationNode> fetchNodeList(String key) - { - return getExpressionEngine().query(getRootNode(), key); - } - - /** - * Checks if the specified node is defined. - * - * @param node the node to be checked - * @return a flag if this node is defined - */ - protected boolean nodeDefined(ConfigurationNode node) - { - DefinedVisitor visitor = new DefinedVisitor(); - node.visit(visitor); - return visitor.isDefined(); - } - - /** - * Removes the specified node from this configuration. This method ensures - * that parent nodes that become undefined by this operation are also - * removed. - * - * @param node the node to be removed - */ - protected void removeNode(ConfigurationNode node) - { - ConfigurationNode parent = node.getParentNode(); - if (parent != null) - { - parent.removeChild(node); - if (!nodeDefined(parent)) - { - removeNode(parent); - } - } - } - - /** - * Clears the value of the specified node. If the node becomes undefined by - * this operation, it is removed from the hierarchy. - * - * @param node the node to be cleared - */ - protected void clearNode(ConfigurationNode node) - { - node.setValue(null); - if (!nodeDefined(node)) - { - removeNode(node); - } - } - - /** - * Creates a new <code>Node</code> object with the specified name. This - * method can be overloaded in derived classes if a specific node type is - * needed. This base implementation always returns a new object of the - * <code>Node</code> class. - * - * @param name the name of the new node - * @return the new node - */ - protected ConfigurationNode createNode(String name) - { - return new DefaultConfigurationNode(name); - } - - /** - * Helper method for processing a node add data object obtained from the - * expression engine. This method will create all new nodes. - * - * @param data the data object - * @return the new node - * @since 1.3 - */ - private ConfigurationNode processNodeAddData(NodeAddData data) - { - ConfigurationNode node = data.getParent(); - - // Create missing nodes on the path - for (String nodeName : data.getPathNodes()) - { - ConfigurationNode child = createNode(nodeName); - node.addChild(child); - node = child; - } - - // Add new target node - ConfigurationNode child = createNode(data.getNewNodeName()); - if (data.isAttribute()) - { - node.addAttribute(child); - } - else - { - node.addChild(child); - } - return child; - } - - /** * Clears all reference fields in a node structure. A configuration node can * store a so-called "reference". The meaning of this data is * determined by a concrete sub class. Typically such references are @@ -980,13 +404,14 @@ * * @param node the root node of the node hierarchy, in which the references * are to be cleared - * @since 1.4 */ - protected static void clearReferences(ConfigurationNode node) + protected void clearReferences(ConfigurationNode node) { - node.visit(new ConfigurationNodeVisitorAdapter() + visit(node, new NodeVisitorAdapter<ConfigurationNode>() { - public void visitBeforeChildren(ConfigurationNode node) + @Override + public void visitBeforeChildren(ConfigurationNode node, + NodeHandler<ConfigurationNode> handler) { node.setReference(null); } @@ -994,129 +419,10 @@ } /** - * A specialized visitor that checks if a node is defined. - * "Defined" in this terms means that the node or at least one of - * its sub nodes is associated with a value. - * - */ - static class DefinedVisitor extends ConfigurationNodeVisitorAdapter - { - /** Stores the defined flag. */ - private boolean defined; - - /** - * Checks if iteration should be stopped. This can be done if the first - * defined node is found. - * - * @return a flag if iteration should be stopped - */ - @Override - public boolean terminate() - { - return isDefined(); - } - - /** - * Visits the node. Checks if a value is defined. - * - * @param node the actual node - */ - @Override - public void visitBeforeChildren(ConfigurationNode node) - { - defined = node.getValue() != null; - } - - /** - * Returns the defined flag. - * - * @return the defined flag - */ - public boolean isDefined() - { - return defined; - } - } - - /** - * A specialized visitor that fills a list with keys that are defined in a - * node hierarchy. - */ - class DefinedKeysVisitor extends ConfigurationNodeVisitorAdapter - { - /** Stores the list to be filled. */ - private Set<String> keyList; - - /** A stack with the keys of the already processed nodes. */ - private Stack<String> parentKeys; - - /** - * Default constructor. - */ - public DefinedKeysVisitor() - { - keyList = new LinkedHashSet<String>(); - parentKeys = new Stack<String>(); - } - - /** - * Creates a new <code>DefinedKeysVisitor</code> instance and sets the - * prefix for the keys to fetch. - * - * @param prefix the prefix - */ - public DefinedKeysVisitor(String prefix) - { - this(); - parentKeys.push(prefix); - } - - /** - * Returns the list with all defined keys. - * - * @return the list with the defined keys - */ - public Set<String> getKeyList() - { - return keyList; - } - - /** - * Visits the node after its children has been processed. Removes this - * node's key from the stack. - * - * @param node the node - */ - @Override - public void visitAfterChildren(ConfigurationNode node) - { - parentKeys.pop(); - } - - /** - * Visits the specified node. If this node has a value, its key is added - * to the internal list. - * - * @param node the node to be visited - */ - @Override - public void visitBeforeChildren(ConfigurationNode node) - { - String parentKey = parentKeys.isEmpty() ? null : (String) parentKeys.peek(); - String key = getExpressionEngine().nodeKey(node, parentKey); - parentKeys.push(key); - if (node.getValue() != null) - { - keyList.add(key); - } - } - } - - /** * A specialized visitor that is able to create a deep copy of a node * hierarchy. */ - static class CloneVisitor extends ConfigurationNodeVisitorAdapter + static class CloneVisitor extends NodeVisitorAdapter<ConfigurationNode> { /** A stack with the actual object to be copied. */ private Stack<ConfigurationNode> copyStack; @@ -1138,7 +444,7 @@ * @param node the node */ @Override - public void visitAfterChildren(ConfigurationNode node) + public void visitAfterChildren(ConfigurationNode node, NodeHandler<ConfigurationNode> handler) { ConfigurationNode copy = copyStack.pop(); if (copyStack.isEmpty()) @@ -1153,21 +459,18 @@ * @param node the node */ @Override - public void visitBeforeChildren(ConfigurationNode node) + public void visitBeforeChildren(ConfigurationNode node, NodeHandler<ConfigurationNode> handler) { ConfigurationNode copy = (ConfigurationNode) node.clone(); copy.setParentNode(null); + for(ConfigurationNode attr : node.getAttributes()) + { + copy.addAttribute((ConfigurationNode) attr.clone()); + } if (!copyStack.isEmpty()) { - if (node.isAttribute()) - { - copyStack.peek().addAttribute(copy); - } - else - { - copyStack.peek().addChild(copy); - } + copyStack.peek().addChild(copy); } copyStack.push(copy); @@ -1199,7 +502,7 @@ * method can perform all steps to integrate the new node into the original * structure. */ - protected abstract static class BuilderVisitor extends ConfigurationNodeVisitorAdapter + protected abstract static class BuilderVisitor extends NodeVisitorAdapter<ConfigurationNode> { /** * Visits the specified node before its children have been traversed. @@ -1207,7 +510,7 @@ * @param node the node to visit */ @Override - public void visitBeforeChildren(ConfigurationNode node) + public void visitBeforeChildren(ConfigurationNode node, NodeHandler<ConfigurationNode> handler) { Collection<ConfigurationNode> subNodes = new LinkedList<ConfigurationNode>(node.getChildren()); subNodes.addAll(node.getAttributes());
Copied: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubConfiguration.java (from r632835, commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubnodeConfiguration.java) URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubConfiguration.java?p2=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubConfiguration.java&p1=commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubnodeConfiguration.java&r1=632835&r2=635081&rev=635081&view=diff ============================================================================== --- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubnodeConfiguration.java (original) +++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/SubConfiguration.java Sat Mar 8 12:42:32 2008 @@ -18,10 +18,9 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; -import org.apache.commons.configuration2.tree.ConfigurationNode; +import org.apache.commons.configuration2.expr.NodeList; /** * <p> @@ -31,71 +30,71 @@ * <p> * Configurations of this type are initialized with a parent configuration and a * configuration node of this configuration. This node becomes the root node of - * the subnode configuration. All property accessor methods are evaluated + * the sub configuration. All property accessor methods are evaluated * relative to this root node. A good use case for a - * <code>SubnodeConfiguration</code> is when multiple properties from a + * <code>SubConfiguration</code> is when multiple properties from a * specific sub tree of the whole configuration need to be accessed. Then a - * <code>SubnodeConfiguration</code> can be created with the parent node of + * <code>SubConfiguration</code> can be created with the parent node of * the affected sub tree as root node. This allows for simpler property keys and * is also more efficient. * </p> * <p> - * A subnode configuration and its parent configuration operate on the same + * A sub configuration and its parent configuration operate on the same * hierarchy of configuration nodes. So if modifications are performed at the - * subnode configuration, these changes are immideately visible in the parent + * sub configuration, these changes are immediately visible in the parent * configuration. Analogously will updates of the parent configuration affect - * the subnode configuration if the sub tree spanned by the subnode + * the sub configuration if the sub tree spanned by the sub * configuration's root node is involved. * </p> * <p> * There are however changes at the parent configuration, which cause the - * subnode configuration to become detached. An example for such a change is a + * sub configuration to become detached. An example for such a change is a * reload operation of a file-based configuration, which replaces all nodes of - * the parent configuration. The subnode configuration per default still - * references the old nodes. Another example are list structures: a subnode + * the parent configuration. The sub configuration per default still + * references the old nodes. Another example are list structures: a sub * configuration can be created to point on the <em>i</em>th element of the * list. Now list elements can be added or removed, so that the list elements' - * indices change. In such a scenario the subnode configuration would always + * indices change. In such a scenario the sub configuration would always * point to the same list element, regardless of its current index. * </p> * <p> - * To solve these problems and make a subnode configuration aware of + * To solve these problems and make a sub configuration aware of * such structural changes of its parent, it is possible to associate a - * subnode configuration with a configuration key. This can be done by calling - * the <code>setSubnodeKey()</code> method. If here a key is set, the subnode + * sub configuration with a configuration key. This can be done by calling + * the <code>setSubnodeKey()</code> method. If here a key is set, the sub * configuration will evaluate it on each access, thus ensuring that it is - * always in sync with its parent. In this mode the subnode configuration really + * always in sync with its parent. In this mode the sub configuration really * behaves like a live-view on its parent. The price for this is a decreased * performance because now an additional evaluation has to be performed on each * property access. So this mode should only be used if necessary; if for - * instance a subnode configuration is only used for a temporary convenient + * instance a sub configuration is only used for a temporary convenient * access to a complex configuration, there is no need to make it aware for - * structural changes of its parent. If a subnode configuration is created - * using the <code>[EMAIL PROTECTED] HierarchicalConfiguration#configurationAt(String, boolean) - * configurationAt()}</code> method of <code>HierarchicalConfiguration</code> + * structural changes of its parent. If a sub configuration is created + * using the <code>[EMAIL PROTECTED] AbstractHierarchicalConfiguration#configurationAt(String, boolean) + * configurationAt()}</code> method of <code>AbstractHierarchicalConfiguration</code> * (which should be the preferred way), with an additional boolean parameter it - * can be specified whether the resulting subnode configuration should be + * can be specified whether the resulting sub configuration should be * aware of structural changes or not. Then the configuration key will be * automatically set. * </p> * <p> - * <em>Note:</em> At the moment support for creating a subnode configuration - * that is aware of structural changes of its parent from another subnode - * configuration (a "sub subnode configuration") is limited. This only works if - * <ol><li>the subnode configuration that serves as the parent for the new - * subnode configuration is itself associated with a configuration key and</li> - * <li>the key passed in to create the new subnode configuration is not too + * <em>Note:</em> At the moment support for creating a sub configuration + * that is aware of structural changes of its parent from another sub + * configuration (a "sub sub configuration") is limited. This only works if + * <ol><li>the sub configuration that serves as the parent for the new + * sub configuration is itself associated with a configuration key and</li> + * <li>the key passed in to create the new sub configuration is not too * complex (if configuration keys are used that contain indices, a corresponding * key that is valid from the parent configuration's point of view cannot be * constructed).</li></ol> * </p> * <p> - * When a subnode configuration is created, it inherits the settings of its + * When a sub configuration is created, it inherits the settings of its * parent configuration, e.g. some flags like the * <code>throwExceptionOnMissing</code> flag or the settings for handling list * delimiters) or the expression engine. If these settings are changed later in - * either the subnode or the parent configuration, the changes are not visible - * for each other. So you could create a subnode configuration, change its + * either the sub or the parent configuration, the changes are not visible + * for each other. So you could create a sub configuration and then change its * expression engine without affecting the parent configuration. * </p> * <p> @@ -103,25 +102,23 @@ * <code>[EMAIL PROTECTED] SubsetConfiguration}</code>. The difference is that a subset * configuration of a hierarchical configuration may combine multiple * configuration nodes from different sub trees of the configuration, while all - * nodes in a subnode configuration belong to the same sub tree. If an + * nodes in a sub configuration belong to the same sub tree. If an * application can live with this limitation, it is recommended to use this * class instead of <code>SubsetConfiguration</code> because creating a subset - * configuration is more expensive than creating a subnode configuration. + * configuration is more expensive than creating a sub configuration. * </p> * - * @since 1.3 + * @since 2.0 * @author Oliver Heger * @version $Id$ */ -public class SubnodeConfiguration extends HierarchicalConfiguration +public class SubConfiguration<T> extends AbstractHierarchicalConfiguration<T> { - /** - * The serial version UID. - */ - private static final long serialVersionUID = 3105734147019386480L; - /** Stores the parent configuration. */ - private HierarchicalConfiguration parent; + private AbstractHierarchicalConfiguration<T> parent; + + /** Stores the root node of this sub configuration.*/ + private T rootNode; /** Stores the key that was used to construct this configuration.*/ private String subnodeKey; @@ -131,10 +128,13 @@ * initializes it with the parent configuration and the new root node. * * @param parent the parent configuration - * @param root the root node of this subnode configuration + * @param root the root node of this sub configuration + * @throws IllegalArgumentException if the parent or the root node are <b>null</b> */ - public SubnodeConfiguration(HierarchicalConfiguration parent, ConfigurationNode root) + public SubConfiguration(AbstractHierarchicalConfiguration<T> parent, T root) { + super((parent != null) ? parent.getNodeHandler() : null); + if (parent == null) { throw new IllegalArgumentException( @@ -145,29 +145,28 @@ throw new IllegalArgumentException("Root node must not be null!"); } - setRootNode(root); + rootNode = root; this.parent = parent; initFromParent(parent); } /** - * Returns the parent configuration of this subnode configuration. + * Returns the parent configuration of this sub configuration. * * @return the parent configuration */ - public HierarchicalConfiguration getParent() + public AbstractHierarchicalConfiguration<T> getParent() { return parent; } /** * Returns the key that was used to construct this configuration. If here a - * non-<b>null</b> value is returned, the subnode configuration will + * non-<b>null</b> value is returned, the sub configuration will * always check its parent for structural changes and reconstruct itself if * necessary. * * @return the key for selecting this configuration's root node - * @since 1.5 */ public String getSubnodeKey() { @@ -175,12 +174,11 @@ } /** - * Sets the key to the root node of this subnode configuration. If here a - * key is set, the subnode configuration will behave like a live-view on its + * Sets the key to the root node of this sub configuration. If here a + * key is set, the sub configuration will behave like a live-view on its * parent for this key. See the class comment for more details. * * @param subnodeKey the key used to construct this configuration - * @since 1.5 */ public void setSubnodeKey(String subnodeKey) { @@ -188,34 +186,34 @@ } /** - * Returns the root node for this configuration. If a subnode key is set, - * this implementation re-evaluates this key to find out if this subnode - * configuration needs to be reconstructed. This ensures that the subnode + * Returns the root node for this configuration. If a sub key is set, + * this implementation re-evaluates this key to find out if this sub + * configuration needs to be reconstructed. This ensures that the sub * configuration is always synchronized with its parent configuration. * * @return the root node of this configuration - * @since 1.5 * @see #setSubnodeKey(String) */ - public ConfigurationNode getRootNode() + @Override + public T getRootNode() { if (getSubnodeKey() != null) { try { - List<ConfigurationNode> nodes = getParent().fetchNodeList(getSubnodeKey()); - if (nodes.size() != 1) + NodeList<T> nodes = getParent().fetchNodeList(getSubnodeKey()); + if (nodes.size() != 1 || !nodes.isNode(0)) { - // key is invalid, so detach this subnode configuration + // key is invalid, so detach this sub configuration setSubnodeKey(null); } else { - ConfigurationNode currentRoot = nodes.get(0); - if (currentRoot != super.getRootNode()) + T currentRoot = nodes.getNode(0); + if (currentRoot != rootNode) { // the root node was changed due to a change of the parent - setRootNode(currentRoot); + rootNode = currentRoot; } return currentRoot; } @@ -229,21 +227,22 @@ } } - return super.getRootNode(); // use stored root node + return rootNode; // use stored root node } /** * Returns a hierarchical configuration object for the given sub node. * This implementation will ensure that the returned - * <code>SubnodeConfiguration</code> object will have the same parent than + * <code>SubConfiguration</code> object will have the same parent as * this object. * * @param node the sub node, for which the configuration is to be created * @return a hierarchical configuration for this sub node */ - protected SubnodeConfiguration createSubnodeConfiguration(ConfigurationNode node) + @Override + protected SubConfiguration<T> createSubnodeConfiguration(T node) { - SubnodeConfiguration result = new SubnodeConfiguration(getParent(), node); + SubConfiguration<T> result = new SubConfiguration<T>(getParent(), node); getParent().registerSubnodeConfiguration(result); return result; } @@ -251,43 +250,43 @@ /** * Returns a hierarchical configuration object for the given sub node that * is aware of structural changes of its parent. Works like the method with - * the same name, but also sets the subnode key for the new subnode + * the same name, but also sets the subnode key for the new sub * configuration, so it can check whether the parent has been changed. This - * only works if this subnode configuration has itself a valid subnode key. - * So if a subnode configuration that should be aware of structural changes - * is created from an already existing subnode configuration, this subnode + * only works if this sub configuration has itself a valid sub key. + * So if a sub configuration that should be aware of structural changes + * is created from an already existing sub configuration, this sub * configuration must also be aware of such changes. * * @param node the sub node, for which the configuration is to be created * @param subnodeKey the construction key * @return a hierarchical configuration for this sub node - * @since 1.5 */ - protected SubnodeConfiguration createSubnodeConfiguration( - ConfigurationNode node, String subnodeKey) + @Override + protected SubConfiguration<T> createSubnodeConfiguration( + T node, String subnodeKey) { - SubnodeConfiguration result = createSubnodeConfiguration(node); + SubConfiguration<T> result = createSubnodeConfiguration(node); if (getSubnodeKey() != null) { // construct the correct subnode key // determine path to root node - List<ConfigurationNode> lstPathToRoot = new ArrayList<ConfigurationNode>(); - ConfigurationNode top = super.getRootNode(); - ConfigurationNode nd = node; + List<T> lstPathToRoot = new ArrayList<T>(); + T top = rootNode; + T nd = node; while (nd != top) { lstPathToRoot.add(nd); - nd = nd.getParentNode(); + nd = getNodeHandler().getParent(nd); } // construct the keys for the nodes on this path Collections.reverse(lstPathToRoot); String key = getSubnodeKey(); - for (Iterator it = lstPathToRoot.iterator(); it.hasNext();) + for (T currentNode : lstPathToRoot) { key = getParent().getExpressionEngine().nodeKey( - (ConfigurationNode) it.next(), key); + currentNode, key, getNodeHandler()); } result.setSubnodeKey(key); } @@ -298,23 +297,24 @@ /** * Creates a new node. This task is delegated to the parent. * + * @param parent the parent node * @param name the node's name * @return the new node */ @Override - protected ConfigurationNode createNode(String name) + protected T createNode(T parent, String name) { - return getParent().createNode(name); + return getParent().createNode(parent, name); } /** - * Initializes this subnode configuration from the given parent + * Initializes this sub configuration from the given parent * configuration. This method is called by the constructor. It will copy * many settings from the parent. * * @param parentConfig the parent configuration */ - protected void initFromParent(HierarchicalConfiguration parentConfig) + protected void initFromParent(AbstractHierarchicalConfiguration<T> parentConfig) { setExpressionEngine(parentConfig.getExpressionEngine()); setListDelimiter(parentConfig.getListDelimiter());