| /* |
| * 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 |
| * |
| * https://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.jupiter.api.Assertions.assertEquals; |
| import static org.junit.jupiter.api.Assertions.assertInstanceOf; |
| import static org.junit.jupiter.api.Assertions.assertNull; |
| import static org.junit.jupiter.api.Assertions.assertThrows; |
| import static org.junit.jupiter.api.Assertions.assertTrue; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.List; |
| |
| import org.apache.commons.configuration2.convert.DefaultListDelimiterHandler; |
| import org.apache.commons.configuration2.ex.ConfigurationRuntimeException; |
| import org.apache.commons.configuration2.tree.DefaultConfigurationKey; |
| import org.apache.commons.configuration2.tree.DefaultExpressionEngine; |
| import org.apache.commons.configuration2.tree.ImmutableNode; |
| import org.apache.commons.configuration2.tree.NodeStructureHelper; |
| import org.junit.jupiter.api.BeforeEach; |
| import org.junit.jupiter.api.Test; |
| |
| /** |
| * Test class for {@code BaseHierarchicalConfiguration}. |
| */ |
| public class TestHierarchicalConfiguration { |
| |
| /** Constant for a changed name. */ |
| private static final String NEW_NAME = "alteredName"; |
| |
| /** |
| * Creates a {@code DefaultConfigurationKey} object. |
| * |
| * @return the new key object |
| */ |
| private static DefaultConfigurationKey createConfigurationKey() { |
| return new DefaultConfigurationKey(DefaultExpressionEngine.INSTANCE); |
| } |
| |
| /** The configuration to be tested. */ |
| private BaseHierarchicalConfiguration config; |
| |
| /** |
| * Tests access to sub configurations as children of a defined node. |
| * |
| * @param withUpdates the updates flag |
| * @param expectedName the expected table name when reading a property |
| */ |
| private void checkChildConfigurationsAtWithUpdates(final boolean withUpdates, final String expectedName) { |
| final String key = "tables.table(0)"; |
| final List<HierarchicalConfiguration<ImmutableNode>> children = withUpdates ? config.childConfigurationsAt(key, true) |
| : config.childConfigurationsAt(key); |
| assertEquals(2, children.size()); |
| final HierarchicalConfiguration<ImmutableNode> sub = children.get(0); |
| sub.setProperty(null, NEW_NAME); |
| assertEquals(expectedName, config.getString(key + ".name")); |
| } |
| |
| /** |
| * Checks configurationAt() if the passed in key selects an attribute. |
| * |
| * @param withUpdates the updates flag |
| */ |
| private void checkConfigurationAtAttributeNode(final boolean withUpdates) { |
| final String key = "tables.table(0)[@type]"; |
| config.addProperty(key, "system"); |
| assertThrows(ConfigurationRuntimeException.class, () -> config.configurationAt(key, withUpdates)); |
| } |
| |
| /** |
| * Helper method for checking a configurationsAt() method. It is also tested whether the configuration is connected to |
| * its parent. |
| * |
| * @param withUpdates the updates flag |
| * @param expName the expected name in the parent configuration |
| */ |
| private void checkConfigurationsAtWithUpdate(final boolean withUpdates, final String expName) { |
| final String key = "tables.table(1).fields.field"; |
| final List<HierarchicalConfiguration<ImmutableNode>> lstFlds = withUpdates ? config.configurationsAt(key, true) : config.configurationsAt(key); |
| checkSubConfigurations(lstFlds); |
| lstFlds.get(0).setProperty("name", NEW_NAME); |
| assertEquals(expName, config.getString("tables.table(1).fields.field(0).name")); |
| } |
| |
| /** |
| * Checks the content of the passed in configuration object. Used by some tests that copy a configuration. |
| * |
| * @param c the configuration to check |
| */ |
| private void checkContent(final Configuration c) { |
| for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) { |
| assertEquals(NodeStructureHelper.table(i), c.getString("tables.table(" + i + ").name")); |
| for (int j = 0; j < NodeStructureHelper.fieldsLength(i); j++) { |
| assertEquals(NodeStructureHelper.field(i, j), c.getString("tables.table(" + i + ").fields.field(" + j + ").name")); |
| } |
| } |
| } |
| |
| /** |
| * Helper method for checking a list of sub configurations pointing to the single fields of the table configuration. |
| * |
| * @param lstFlds the list with sub configurations |
| */ |
| private void checkSubConfigurations(final List<? extends ImmutableConfiguration> lstFlds) { |
| assertEquals(NodeStructureHelper.fieldsLength(1), lstFlds.size()); |
| for (int i = 0; i < NodeStructureHelper.fieldsLength(1); i++) { |
| final ImmutableConfiguration sub = lstFlds.get(i); |
| assertEquals(NodeStructureHelper.field(1, i), sub.getString("name"), "Wrong field at position " + i); |
| } |
| } |
| |
| @BeforeEach |
| public void setUp() throws Exception { |
| final ImmutableNode root = new ImmutableNode.Builder(1).addChild(NodeStructureHelper.ROOT_TABLES_TREE).create(); |
| config = new BaseHierarchicalConfiguration(); |
| config.getNodeModel().setRootNode(root); |
| } |
| |
| /** |
| * Tests the result of childConfigurationsAt() if the key does not point to an existing node. |
| */ |
| @Test |
| void testChildConfigurationsAtNotFound() { |
| assertTrue(config.childConfigurationsAt("not.existing.key").isEmpty()); |
| } |
| |
| /** |
| * Tests the result of childConfigurationsAt() if the key selects multiple nodes. |
| */ |
| @Test |
| void testChildConfigurationsAtNoUniqueKey() { |
| assertTrue(config.childConfigurationsAt("tables.table").isEmpty()); |
| } |
| |
| /** |
| * Tests whether sub configurations for the children of a given node can be queried if no updates are propagated. |
| */ |
| @Test |
| void testChildConfigurationsAtNoUpdates() { |
| checkChildConfigurationsAtWithUpdates(false, NodeStructureHelper.table(0)); |
| } |
| |
| /** |
| * Tests whether sub configurations for the children of a given node can be queried that support updates. |
| */ |
| @Test |
| void testChildConfigurationsAtWithUpdates() { |
| checkChildConfigurationsAtWithUpdates(true, NEW_NAME); |
| } |
| |
| @Test |
| void testClone() { |
| final Configuration copy = (Configuration) config.clone(); |
| assertInstanceOf(BaseHierarchicalConfiguration.class, copy); |
| config.setProperty("tables.table(0).name", "changed table name"); |
| checkContent(copy); |
| } |
| |
| /** |
| * Tests configurationAt() if the passed in key selects an attribute result. |
| */ |
| @Test |
| void testConfigurationAtAttributeNode() { |
| checkConfigurationAtAttributeNode(false); |
| } |
| |
| /** |
| * Tests configurationAt() if the passed in key selects an attribute result and the updates flag is set. |
| */ |
| @Test |
| void testConfigurationAtAttributeNodeWithUpdates() { |
| checkConfigurationAtAttributeNode(true); |
| } |
| |
| /** |
| * Tests whether a {@code SubnodeConfiguration} can be cleared and its root node can be removed from its parent |
| * configuration. |
| */ |
| @Test |
| void testConfigurationAtClearAndDetach() { |
| config.addProperty("test.sub.test", "success"); |
| config.addProperty("test.other", "check"); |
| final HierarchicalConfiguration<ImmutableNode> sub = config.configurationAt("test.sub", true); |
| sub.clear(); |
| assertTrue(sub.isEmpty()); |
| assertNull(config.getString("test.sub.test")); |
| sub.setProperty("test", "failure!"); |
| assertNull(config.getString("test.sub.test")); |
| } |
| |
| /** |
| * Tests the configurationAt() method if the passed in key selects multiple nodes. This should cause an exception. |
| */ |
| @Test |
| void testConfigurationAtMultipleNodes() { |
| assertThrows(ConfigurationRuntimeException.class, () -> config.configurationAt("tables.table.name")); |
| } |
| |
| /** |
| * Tests configurationAt() if the passed in key selects multiple nodes and the update flag is set. |
| */ |
| @Test |
| void testConfigurationAtMultipleNodesWithUpdates() { |
| assertThrows(ConfigurationRuntimeException.class, () -> config.configurationAt("tables.table.name", true)); |
| } |
| |
| /** |
| * Tests whether a configuration obtained via configurationAt() contains the expected properties. |
| */ |
| @Test |
| void testConfigurationAtReadAccess() { |
| final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt("tables.table(1)"); |
| assertEquals(NodeStructureHelper.table(1), subConfig.getString("name")); |
| final List<Object> lstFlds = subConfig.getList("fields.field.name"); |
| |
| final List<String> expected = new ArrayList<>(); |
| for (int i = 0; i < NodeStructureHelper.fieldsLength(1); i++) { |
| expected.add(NodeStructureHelper.field(1, i)); |
| } |
| assertEquals(expected, lstFlds); |
| } |
| |
| /** |
| * Tests the configurationAt() method if the passed in key does not exist. |
| */ |
| @Test |
| void testConfigurationAtUnknownSubTree() { |
| assertThrows(ConfigurationRuntimeException.class, () -> config.configurationAt("non.existing.key")); |
| } |
| |
| /** |
| * Tests configurationAt() for a non existing key if the update flag is set. |
| */ |
| @Test |
| void testConfigurationAtUnknownSubTreeWithUpdates() { |
| assertThrows(ConfigurationRuntimeException.class, () -> config.configurationAt("non.existing.key", true)); |
| } |
| |
| /** |
| * Tests an update operation on a parent configuration if the sub configuration is connected. |
| */ |
| @Test |
| void testConfigurationAtUpdateParentConnected() { |
| final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt("tables.table(1)", true); |
| config.setProperty("tables.table(1).fields.field(2).name", "testField"); |
| assertEquals("testField", subConfig.getString("fields.field(2).name")); |
| } |
| |
| /** |
| * Tests an update operation on a parent configuration if the sub configuration is independent. |
| */ |
| @Test |
| void testConfigurationAtUpdateParentIndependent() { |
| final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt("tables.table(1)"); |
| config.setProperty("tables.table(1).fields.field(2).name", "testField"); |
| assertEquals(NodeStructureHelper.field(1, 2), subConfig.getString("fields.field(2).name")); |
| } |
| |
| /** |
| * Tests an update operation on a sub configuration which is connected to its parent. |
| */ |
| @Test |
| void testConfigurationAtUpdateSubConfigConnected() { |
| final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt("tables.table(1)", true); |
| subConfig.setProperty("name", "testTable"); |
| assertEquals("testTable", config.getString("tables.table(1).name")); |
| } |
| |
| /** |
| * Tests an update operation on a sub configuration which is independent on its parent. |
| */ |
| @Test |
| void testConfigurationAtUpdateSubConfigIndependent() { |
| final HierarchicalConfiguration<ImmutableNode> subConfig = config.configurationAt("tables.table(1)"); |
| subConfig.setProperty("name", "testTable"); |
| assertEquals("testTable", subConfig.getString("name")); |
| assertEquals(NodeStructureHelper.table(1), config.getString("tables.table(1).name")); |
| } |
| |
| /** |
| * Tests whether a connected configuration is correctly initialized with properties of its parent. |
| */ |
| @Test |
| void testConfigurationAtWithUpdateInitialized() { |
| final String key = "tables.table"; |
| config.setListDelimiterHandler(new DefaultListDelimiterHandler(';')); |
| config.setThrowExceptionOnMissing(true); |
| final List<HierarchicalConfiguration<ImmutableNode>> subs = config.configurationsAt(key, true); |
| final BaseHierarchicalConfiguration sub = (BaseHierarchicalConfiguration) subs.get(0); |
| assertEquals(config.getListDelimiterHandler(), sub.getListDelimiterHandler()); |
| assertTrue(sub.isThrowExceptionOnMissing()); |
| } |
| |
| /** |
| * Tests configurationsAt() if an attribute key is passed in. |
| */ |
| @Test |
| void testConfigurationsAtAttributeKey() { |
| final String attrKey = "tables.table(0)[@type]"; |
| config.addProperty(attrKey, "user"); |
| assertTrue(config.configurationsAt(attrKey).isEmpty()); |
| } |
| |
| /** |
| * Tests the configurationsAt() method when the passed in key does not select any sub nodes. |
| */ |
| @Test |
| void testConfigurationsAtEmpty() { |
| assertTrue(config.configurationsAt("unknown.key").isEmpty()); |
| } |
| |
| /** |
| * Tests the configurationsAt() method if the sub configurations are not connected. |
| */ |
| @Test |
| void testConfigurationsAtNoUpdate() { |
| checkConfigurationsAtWithUpdate(false, NodeStructureHelper.field(1, 0)); |
| } |
| |
| /** |
| * Tests configurationsAt() if the sub configurations are connected. |
| */ |
| @Test |
| void testConfigurationsAtWithUpdates() { |
| checkConfigurationsAtWithUpdate(true, NEW_NAME); |
| } |
| |
| /** |
| * Tests whether immutable configurations for the children of a given node can be queried. |
| */ |
| @Test |
| void testImmutableChildConfigurationsAt() { |
| final List<ImmutableHierarchicalConfiguration> children = config.immutableChildConfigurationsAt("tables.table(0)"); |
| assertEquals(2, children.size()); |
| final ImmutableHierarchicalConfiguration c1 = children.get(0); |
| assertEquals("name", c1.getRootElementName()); |
| assertEquals(NodeStructureHelper.table(0), c1.getString(null)); |
| final ImmutableHierarchicalConfiguration c2 = children.get(1); |
| assertEquals("fields", c2.getRootElementName()); |
| assertEquals(NodeStructureHelper.field(0, 0), c2.getString("field(0).name")); |
| } |
| |
| /** |
| * Tests whether an immutable configuration for a sub tree can be obtained. |
| */ |
| @Test |
| void testImmutableConfigurationAt() { |
| final ImmutableHierarchicalConfiguration subConfig = config.immutableConfigurationAt("tables.table(1)"); |
| assertEquals(NodeStructureHelper.table(1), subConfig.getString("name")); |
| final List<Object> lstFlds = subConfig.getList("fields.field.name"); |
| |
| final List<String> expected = new ArrayList<>(); |
| for (int i = 0; i < NodeStructureHelper.fieldsLength(1); i++) { |
| expected.add(NodeStructureHelper.field(1, i)); |
| } |
| assertEquals(expected, lstFlds); |
| } |
| |
| /** |
| * Tests whether the support updates flag is taken into account when creating an immutable sub configuration. |
| */ |
| @Test |
| void testImmutableConfigurationAtSupportUpdates() { |
| final String newTableName = NodeStructureHelper.table(1) + "_other"; |
| final ImmutableHierarchicalConfiguration subConfig = config.immutableConfigurationAt("tables.table(1)", true); |
| config.addProperty("tables.table(-1).name", newTableName); |
| config.clearTree("tables.table(1)"); |
| assertEquals(newTableName, subConfig.getString("name")); |
| } |
| |
| /** |
| * Tests whether a list of immutable sub configurations can be queried. |
| */ |
| @Test |
| void testImmutableConfigurationsAt() { |
| final List<ImmutableHierarchicalConfiguration> lstFlds = config.immutableConfigurationsAt("tables.table(1).fields.field"); |
| checkSubConfigurations(lstFlds); |
| } |
| |
| /** |
| * Tests the copy constructor. |
| */ |
| @Test |
| void testInitCopy() { |
| final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration(config); |
| checkContent(copy); |
| } |
| |
| /** |
| * Tests the copy constructor when a null reference is passed. |
| */ |
| @Test |
| void testInitCopyNull() { |
| final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration((HierarchicalConfiguration<ImmutableNode>) null); |
| assertTrue(copy.isEmpty()); |
| } |
| |
| /** |
| * Tests whether the nodes of a copied configuration are independent from the source configuration. |
| */ |
| @Test |
| void testInitCopyUpdate() { |
| final BaseHierarchicalConfiguration copy = new BaseHierarchicalConfiguration(config); |
| config.setProperty("tables.table(0).name", "NewTable"); |
| checkContent(copy); |
| } |
| |
| /** |
| * Tests obtaining a configuration with all variables substituted. |
| */ |
| @Test |
| void testInterpolatedConfiguration() { |
| config.setListDelimiterHandler(new DefaultListDelimiterHandler(',')); |
| final BaseHierarchicalConfiguration c = (BaseHierarchicalConfiguration) InterpolationTestHelper.testInterpolatedConfiguration(config); |
| |
| // tests whether the hierarchical structure has been maintained |
| checkContent(c); |
| } |
| |
| /** |
| * Tests whether interpolation works on an empty configuration. |
| */ |
| @Test |
| void testInterpolatedConfigurationEmpty() { |
| config = new BaseHierarchicalConfiguration(); |
| assertTrue(config.interpolatedConfiguration().isEmpty()); |
| } |
| |
| /** |
| * Tests interpolation with a subset. |
| */ |
| @Test |
| void testInterpolationSubset() { |
| InterpolationTestHelper.testInterpolationSubset(config); |
| } |
| |
| /** |
| * Tests whether interpolation with a subset configuration works over multiple layers. |
| */ |
| @Test |
| void testInterpolationSubsetMultipleLayers() { |
| config.clear(); |
| config.addProperty("var", "value"); |
| config.addProperty("prop2.prop[@attr]", "${var}"); |
| final Configuration sub1 = config.subset("prop2"); |
| final Configuration sub2 = sub1.subset("prop"); |
| assertEquals("value", sub2.getString("[@attr]")); |
| } |
| |
| @Test |
| void testSubset() { |
| // test the subset on the first table |
| Configuration subset = config.subset("tables.table(0)"); |
| assertEquals(NodeStructureHelper.table(0), subset.getProperty("name")); |
| |
| Object prop = subset.getProperty("fields.field.name"); |
| Collection<?> collection = assertInstanceOf(Collection.class, prop); |
| assertEquals(5, collection.size()); |
| |
| for (int i = 0; i < NodeStructureHelper.fieldsLength(0); i++) { |
| final DefaultConfigurationKey key = createConfigurationKey(); |
| key.append("fields").append("field").appendIndex(i); |
| key.append("name"); |
| assertEquals(NodeStructureHelper.field(0, i), subset.getProperty(key.toString())); |
| } |
| |
| // test the subset on the second table |
| assertTrue(config.subset("tables.table(2)").isEmpty()); |
| |
| // test the subset on the fields |
| subset = config.subset("tables.table.fields.field"); |
| prop = subset.getProperty("name"); |
| collection = assertInstanceOf(Collection.class, prop); |
| int expectedFieldCount = 0; |
| for (int i = 0; i < NodeStructureHelper.tablesLength(); i++) { |
| expectedFieldCount += NodeStructureHelper.fieldsLength(i); |
| } |
| assertEquals(expectedFieldCount, collection.size()); |
| |
| assertEquals(NodeStructureHelper.field(0, 0), subset.getProperty("name(0)")); |
| |
| // test the subset on the field names |
| subset = config.subset("tables.table.fields.field.name"); |
| assertTrue(subset.isEmpty()); |
| } |
| |
| /** |
| * Tests subset() if the passed in key selects an attribute. |
| */ |
| @Test |
| void testSubsetAttributeResult() { |
| final String key = "tables.table(0)[@type]"; |
| config.addProperty(key, "system"); |
| final BaseHierarchicalConfiguration subset = (BaseHierarchicalConfiguration) config.subset(key); |
| assertTrue(subset.getModel().getNodeHandler().getRootNode().getChildren().isEmpty()); |
| assertEquals("system", subset.getString("[@type]")); |
| } |
| |
| /** |
| * Tests the subset() method if the specified key selects multiple keys. The resulting root node should have a value |
| * only if exactly one of the selected nodes has a value. Related to CONFIGURATION-295. |
| */ |
| @Test |
| void testSubsetMultipleNodesWithValues() { |
| config.setProperty("tables.table(0).fields", "My fields"); |
| Configuration subset = config.subset("tables.table.fields"); |
| assertEquals("My fields", subset.getString("")); |
| config.setProperty("tables.table(1).fields", "My other fields"); |
| subset = config.subset("tables.table.fields"); |
| assertNull(subset.getString("")); |
| } |
| |
| /** |
| * Tests the subset() method if the specified node has a value. This value must be available in the subset, too. Related |
| * to CONFIGURATION-295. |
| */ |
| @Test |
| void testSubsetNodeWithValue() { |
| config.setProperty("tables.table(0).fields", "My fields"); |
| final Configuration subset = config.subset("tables.table(0).fields"); |
| assertEquals(NodeStructureHelper.field(0, 0), subset.getString("field(0).name")); |
| assertEquals("My fields", subset.getString("")); |
| } |
| } |