| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.commons.configuration2; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertNotSame; |
| import static org.junit.Assert.assertSame; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.util.Collections; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.NoSuchElementException; |
| import java.util.Set; |
| |
| import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler; |
| import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; |
| import org.apache.commons.configuration2.convert.ListDelimiterHandler; |
| import org.apache.commons.configuration2.interpol.ConfigurationInterpolator; |
| import org.apache.commons.configuration2.interpol.Lookup; |
| import org.apache.commons.configuration2.tree.ImmutableNode; |
| import org.apache.commons.configuration2.tree.InMemoryNodeModel; |
| import org.apache.commons.configuration2.tree.NodeSelector; |
| import org.apache.commons.configuration2.tree.NodeStructureHelper; |
| import org.apache.commons.configuration2.tree.TrackedNodeModel; |
| import org.apache.commons.configuration2.tree.xpath.XPathExpressionEngine; |
| import org.easymock.EasyMock; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| /** |
| * Test case for SubnodeConfiguration. |
| * |
| * @version $Id$ |
| */ |
| public class TestSubnodeConfiguration |
| { |
| /** The key used for the SubnodeConfiguration. */ |
| private static final String SUB_KEY = "tables.table(0)"; |
| |
| /** The selector used by the test configuration. */ |
| private static final NodeSelector SELECTOR = new NodeSelector(SUB_KEY); |
| |
| /** The parent configuration. */ |
| private BaseHierarchicalConfiguration parent; |
| |
| /** The subnode configuration to be tested. */ |
| private SubnodeConfiguration config; |
| |
| @Before |
| public void setUp() throws Exception |
| { |
| parent = setUpParentConfig(); |
| } |
| |
| /** |
| * Initializes the parent configuration. This method creates the typical |
| * structure of tables and fields nodes. |
| * |
| * @return the parent configuration |
| */ |
| private static BaseHierarchicalConfiguration setUpParentConfig() |
| { |
| BaseHierarchicalConfiguration conf = |
| new BaseHierarchicalConfiguration(); |
| appendTree(conf, NodeStructureHelper.ROOT_TABLES_TREE); |
| return conf; |
| } |
| |
| /** |
| * Adds a tree structure to the root node of the given configuration. |
| * |
| * @param configuration the configuration |
| * @param root the root of the tree structure to be added |
| */ |
| private static void appendTree(BaseHierarchicalConfiguration configuration, |
| ImmutableNode root) |
| { |
| configuration.addNodes(null, Collections.singleton(root)); |
| } |
| |
| /** |
| * Performs a standard initialization of the subnode config to test. |
| */ |
| private void setUpSubnodeConfig() |
| { |
| setUpSubnodeConfig(SUB_KEY); |
| } |
| |
| /** |
| * Initializes the test configuration using the specified key. |
| * |
| * @param key the key |
| */ |
| private void setUpSubnodeConfig(String key) |
| { |
| config = (SubnodeConfiguration) parent.configurationAt(key, true); |
| } |
| |
| /** |
| * Sets up the tracked model for the sub configuration. |
| * |
| * @param selector the selector |
| * @return the tracked model |
| */ |
| private TrackedNodeModel setUpTrackedModel(NodeSelector selector) |
| { |
| InMemoryNodeModel parentModel = (InMemoryNodeModel) parent.getModel(); |
| parentModel.trackNode(selector, parent); |
| return new TrackedNodeModel(parent, selector, true); |
| } |
| |
| /** |
| * Tests creation of a subnode config. |
| */ |
| @Test |
| public void testInitSubNodeConfig() |
| { |
| setUpSubnodeConfig(); |
| assertSame( |
| "Wrong root node in subnode", |
| NodeStructureHelper.nodeForKey(parent.getModel() |
| .getNodeHandler().getRootNode(), "tables/table(0)"), |
| config.getModel().getNodeHandler().getRootNode()); |
| assertSame("Wrong parent config", parent, config.getParent()); |
| } |
| |
| /** |
| * Tests constructing a subnode configuration with a null parent. This |
| * should cause an exception. |
| */ |
| @Test(expected = IllegalArgumentException.class) |
| public void testInitSubNodeConfigWithNullParent() |
| { |
| config = |
| new SubnodeConfiguration(null, setUpTrackedModel(SELECTOR) |
| ); |
| } |
| |
| /** |
| * Tests constructing a subnode configuration with a null node model. This |
| * should cause an exception. |
| */ |
| @Test(expected = IllegalArgumentException.class) |
| public void testInitSubNodeConfigWithNullNode() |
| { |
| config = new SubnodeConfiguration(parent, null); |
| } |
| |
| /** |
| * Tests if properties of the sub node can be accessed. |
| */ |
| @Test |
| public void testGetProperties() |
| { |
| setUpSubnodeConfig(); |
| checkSubConfigContent(); |
| } |
| |
| /** |
| * Checks whether the sub configuration has the expected content. |
| */ |
| private void checkSubConfigContent() |
| { |
| assertEquals("Wrong table name", NodeStructureHelper.table(0), |
| config.getString("name")); |
| List<Object> fields = config.getList("fields.field.name"); |
| assertEquals("Wrong number of fields", |
| NodeStructureHelper.fieldsLength(0), fields.size()); |
| for (int i = 0; i < NodeStructureHelper.fieldsLength(0); i++) |
| { |
| assertEquals("Wrong field at position " + i, |
| NodeStructureHelper.field(0, i), fields.get(i)); |
| } |
| } |
| |
| /** |
| * Tests setting of properties in both the parent and the subnode |
| * configuration and whether the changes are visible to each other. |
| */ |
| @Test |
| public void testSetProperty() |
| { |
| setUpSubnodeConfig(); |
| config.setProperty(null, "testTable"); |
| config.setProperty("name", NodeStructureHelper.table(0) + "_tested"); |
| assertEquals("Root value was not set", "testTable", |
| parent.getString("tables.table(0)")); |
| assertEquals("Table name was not changed", NodeStructureHelper.table(0) |
| + "_tested", parent.getString("tables.table(0).name")); |
| |
| parent.setProperty("tables.table(0).fields.field(1).name", "testField"); |
| assertEquals("Field name was not changed", "testField", |
| config.getString("fields.field(1).name")); |
| } |
| |
| /** |
| * Tests adding of properties. |
| */ |
| @Test |
| public void testAddProperty() |
| { |
| setUpSubnodeConfig(); |
| config.addProperty("[@table-type]", "test"); |
| assertEquals("Attribute not set", "test", |
| parent.getString("tables.table(0)[@table-type]")); |
| |
| parent.addProperty("tables.table(0).fields.field(-1).name", "newField"); |
| List<Object> fields = config.getList("fields.field.name"); |
| assertEquals("New field was not added", |
| NodeStructureHelper.fieldsLength(0) + 1, fields.size()); |
| assertEquals("Wrong last field", "newField", |
| fields.get(fields.size() - 1)); |
| } |
| |
| /** |
| * Tests listing the defined keys. |
| */ |
| @Test |
| public void testGetKeys() |
| { |
| setUpSubnodeConfig(); |
| Set<String> keys = new HashSet<String>(); |
| keys.addAll(ConfigurationAssert.keysToList(config)); |
| assertEquals("Incorrect number of keys", 2, keys.size()); |
| assertTrue("Key 1 not contained", keys.contains("name")); |
| assertTrue("Key 2 not contained", keys.contains("fields.field.name")); |
| } |
| |
| /** |
| * Tests setting the exception on missing flag. The subnode config obtains |
| * this flag from its parent. |
| */ |
| @Test(expected = NoSuchElementException.class) |
| public void testSetThrowExceptionOnMissing() |
| { |
| parent.setThrowExceptionOnMissing(true); |
| setUpSubnodeConfig(); |
| assertTrue("Exception flag not fetchted from parent", |
| config.isThrowExceptionOnMissing()); |
| config.getString("non existing key"); |
| } |
| |
| /** |
| * Tests whether the exception flag can be set independently from the |
| * parent. |
| */ |
| @Test |
| public void testSetThrowExceptionOnMissingAffectsParent() |
| { |
| parent.setThrowExceptionOnMissing(true); |
| setUpSubnodeConfig(); |
| config.setThrowExceptionOnMissing(false); |
| assertTrue("Exception flag reset on parent", |
| parent.isThrowExceptionOnMissing()); |
| } |
| |
| /** |
| * Tests manipulating the list delimiter handler. This object is derived |
| * from the parent. |
| */ |
| @Test |
| public void testSetListDelimiterHandler() |
| { |
| ListDelimiterHandler handler1 = new DefaultListDelimiterHandler('/'); |
| ListDelimiterHandler handler2 = new DefaultListDelimiterHandler(';'); |
| parent.setListDelimiterHandler(handler1); |
| setUpSubnodeConfig(); |
| parent.setListDelimiterHandler(handler2); |
| assertEquals("List delimiter handler not obtained from parent", |
| handler1, config.getListDelimiterHandler()); |
| config.addProperty("newProp", "test1,test2/test3"); |
| assertEquals("List was incorrectly splitted", "test1,test2", |
| parent.getString("tables.table(0).newProp")); |
| config.setListDelimiterHandler(DisabledListDelimiterHandler.INSTANCE); |
| assertEquals("List delimiter changed on parent", handler2, |
| parent.getListDelimiterHandler()); |
| } |
| |
| /** |
| * Tests changing the expression engine. |
| */ |
| @Test |
| public void testSetExpressionEngine() |
| { |
| parent.setExpressionEngine(new XPathExpressionEngine()); |
| setUpSubnodeConfig("tables/table[1]"); |
| assertEquals("Wrong field name", NodeStructureHelper.field(0, 1), |
| config.getString("fields/field[2]/name")); |
| Set<String> keys = ConfigurationAssert.keysToSet(config); |
| assertEquals("Wrong number of keys", 2, keys.size()); |
| assertTrue("Key 1 not contained", keys.contains("name")); |
| assertTrue("Key 2 not contained", keys.contains("fields/field/name")); |
| config.setExpressionEngine(null); |
| assertTrue("Expression engine reset on parent", |
| parent.getExpressionEngine() instanceof XPathExpressionEngine); |
| } |
| |
| /** |
| * Tests the configurationAt() method if updates are not supported. |
| */ |
| @Test |
| public void testConfiguarationAtNoUpdates() |
| { |
| setUpSubnodeConfig(); |
| HierarchicalConfiguration<ImmutableNode> sub2 = |
| config.configurationAt("fields.field(1)"); |
| assertEquals("Wrong value of property", |
| NodeStructureHelper.field(0, 1), sub2.getString("name")); |
| parent.setProperty("tables.table(0).fields.field(1).name", "otherName"); |
| assertEquals("Change of parent is visible", |
| NodeStructureHelper.field(0, 1), sub2.getString("name")); |
| } |
| |
| /** |
| * Tests configurationAt() if updates are supported. |
| */ |
| @Test |
| public void testConfigurationAtWithUpdateSupport() |
| { |
| setUpSubnodeConfig(); |
| SubnodeConfiguration sub2 = |
| (SubnodeConfiguration) config.configurationAt("fields.field(1)", true); |
| assertEquals("Wrong value of property", |
| NodeStructureHelper.field(0, 1), sub2.getString("name")); |
| assertEquals("Wrong parent", config, sub2.getParent()); |
| } |
| |
| /** |
| * Tests interpolation features. The subnode config should use its parent |
| * for interpolation. |
| */ |
| @Test |
| public void testInterpolation() |
| { |
| parent.addProperty("tablespaces.tablespace.name", "default"); |
| parent.addProperty("tablespaces.tablespace(-1).name", "test"); |
| parent.addProperty("tables.table(0).tablespace", |
| "${tablespaces.tablespace(0).name}"); |
| assertEquals("Wrong interpolated tablespace", "default", |
| parent.getString("tables.table(0).tablespace")); |
| |
| setUpSubnodeConfig(); |
| assertEquals("Wrong interpolated tablespace in subnode", "default", |
| config.getString("tablespace")); |
| } |
| |
| /** |
| * Helper method for testing interpolation facilities between a sub and its |
| * parent configuration. |
| * |
| * @param withUpdates the supports updates flag |
| */ |
| private void checkInterpolationFromConfigurationAt(boolean withUpdates) |
| { |
| parent.addProperty("base.dir", "/home/foo"); |
| parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1"); |
| parent.addProperty("test.absolute.dir.dir2", "${base.dir}/path2"); |
| parent.addProperty("test.absolute.dir.dir3", "${base.dir}/path3"); |
| |
| Configuration sub = |
| parent.configurationAt("test.absolute.dir", withUpdates); |
| for (int i = 1; i < 4; i++) |
| { |
| assertEquals("Wrong interpolation in parent", "/home/foo/path" + i, |
| parent.getString("test.absolute.dir.dir" + i)); |
| assertEquals("Wrong interpolation in sub", "/home/foo/path" + i, |
| sub.getString("dir" + i)); |
| } |
| } |
| |
| /** |
| * Tests whether interpolation works for a sub configuration obtained via |
| * configurationAt() if updates are not supported. |
| */ |
| @Test |
| public void testInterpolationFromConfigurationAtNoUpdateSupport() |
| { |
| checkInterpolationFromConfigurationAt(false); |
| } |
| |
| /** |
| * Tests whether interpolation works for a sub configuration obtained via |
| * configurationAt() if updates are supported. |
| */ |
| @Test |
| public void testInterpolationFromConfigurationAtWithUpdateSupport() |
| { |
| checkInterpolationFromConfigurationAt(true); |
| } |
| |
| /** |
| * An additional test for interpolation when the configurationAt() method is |
| * involved for a local interpolation. |
| */ |
| @Test |
| public void testLocalInterpolationFromConfigurationAt() |
| { |
| parent.addProperty("base.dir", "/home/foo"); |
| parent.addProperty("test.absolute.dir.dir1", "${base.dir}/path1"); |
| parent.addProperty("test.absolute.dir.dir2", "${dir1}"); |
| |
| Configuration sub = parent.configurationAt("test.absolute.dir"); |
| assertEquals("Wrong interpolation in subnode", "/home/foo/path1", |
| sub.getString("dir1")); |
| assertEquals("Wrong local interpolation in subnode", "/home/foo/path1", |
| sub.getString("dir2")); |
| } |
| |
| /** |
| * Tests manipulating the interpolator. |
| */ |
| @Test |
| public void testInterpolator() |
| { |
| parent.addProperty("tablespaces.tablespace.name", "default"); |
| parent.addProperty("tablespaces.tablespace(-1).name", "test"); |
| |
| setUpSubnodeConfig(); |
| InterpolationTestHelper.testGetInterpolator(config); |
| } |
| |
| @Test |
| public void testLocalLookupsInInterpolatorAreInherited() |
| { |
| parent.addProperty("tablespaces.tablespace.name", "default"); |
| parent.addProperty("tablespaces.tablespace(-1).name", "test"); |
| parent.addProperty("tables.table(0).var", "${brackets:x}"); |
| |
| ConfigurationInterpolator interpolator = parent.getInterpolator(); |
| interpolator.registerLookup("brackets", new Lookup() { |
| |
| public String lookup(String key) { |
| return "(" + key + ")"; |
| } |
| |
| }); |
| setUpSubnodeConfig(); |
| assertEquals("Local lookup was not inherited", "(x)", |
| config.getString("var", "")); |
| } |
| |
| /** |
| * Tests a manipulation of the parent configuration that causes the subnode |
| * configuration to become invalid. In this case the sub config should be |
| * detached and keep its old values. |
| */ |
| @Test |
| public void testParentChangeDetach() |
| { |
| setUpSubnodeConfig(); |
| parent.clear(); |
| checkSubConfigContent(); |
| } |
| |
| /** |
| * Tests detaching a subnode configuration if an exception is thrown during |
| * reconstruction. This can happen e.g. if the expression engine is changed |
| * for the parent. |
| */ |
| @Test |
| public void testParentChangeDetatchException() |
| { |
| setUpSubnodeConfig(); |
| parent.setExpressionEngine(new XPathExpressionEngine()); |
| parent.addProperty("newProp", "value"); |
| checkSubConfigContent(); |
| } |
| |
| /** |
| * Tests whether a clone of a sub configuration can be created. |
| */ |
| @Test |
| public void testClone() |
| { |
| setUpSubnodeConfig(); |
| SubnodeConfiguration copy = (SubnodeConfiguration) config.clone(); |
| assertNotSame("Same model", config.getModel(), copy.getModel()); |
| TrackedNodeModel subModel = (TrackedNodeModel) copy.getModel(); |
| assertEquals("Wrong selector", SELECTOR, subModel.getSelector()); |
| InMemoryNodeModel parentModel = (InMemoryNodeModel) parent.getModel(); |
| assertEquals("Wrong parent model", parentModel, |
| subModel.getParentModel()); |
| |
| // Check whether the track count was increased |
| parentModel.untrackNode(SELECTOR); |
| parentModel.untrackNode(SELECTOR); |
| assertTrue("Wrong finalize flag", |
| subModel.isReleaseTrackedNodeOnFinalize()); |
| } |
| |
| /** |
| * Tests whether the configuration can be closed. |
| */ |
| @Test |
| public void testClose() |
| { |
| TrackedNodeModel model = EasyMock.createMock(TrackedNodeModel.class); |
| EasyMock.expect(model.getSelector()).andReturn(SELECTOR).anyTimes(); |
| model.close(); |
| EasyMock.replay(model); |
| |
| SubnodeConfiguration config = new SubnodeConfiguration(parent, model); |
| config.close(); |
| EasyMock.verify(model); |
| } |
| } |