You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2008/11/18 22:29:33 UTC
svn commit: r718717 - in
/commons/proper/configuration/branches/configuration2_experimental:
src/main/java/org/apache/commons/configuration2/INIConfiguration.java
src/test/java/org/apache/commons/configuration2/TestINIConfiguration.java
xdocs/changes.xml
Author: oheger
Date: Tue Nov 18 13:29:32 2008
New Revision: 718717
URL: http://svn.apache.org/viewvc?rev=718717&view=rev
Log:
Ported HierarchicalINIConfiguration to configuration2 branch. The class is here called INIConfiguration again.
Modified:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/INIConfiguration.java
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestINIConfiguration.java
commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml
Modified: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/INIConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/INIConfiguration.java?rev=718717&r1=718716&r2=718717&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/INIConfiguration.java (original)
+++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/INIConfiguration.java Tue Nov 18 13:29:32 2008
@@ -14,7 +14,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-
package org.apache.commons.configuration2;
import java.io.BufferedReader;
@@ -26,24 +25,31 @@
import java.net.URL;
import java.util.Collection;
import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Set;
-import java.util.TreeSet;
+import org.apache.commons.configuration2.expr.ExpressionEngine;
+import org.apache.commons.configuration2.tree.ConfigurationNode;
+import org.apache.commons.configuration2.tree.DefaultConfigurationNode;
+import org.apache.commons.configuration2.tree.ViewNode;
import org.apache.commons.lang.StringUtils;
/**
* <p>
+ * A specialized hierarchical configuration implementation for parsing ini
+ * files.
+ * </p>
+ * <p>
* An initialization or ini file is a configuration file typically found on
* Microsoft's Windows operating system and contains data for Windows based
* applications.
* </p>
- *
* <p>
* Although popularized by Windows, ini files can be used on any system or
* platform due to the fact that they are merely text files that can easily be
* parsed and modified by both humans and computers.
* </p>
- *
* <p>
* A typcial ini file could look something like:
* </p>
@@ -52,46 +58,42 @@
* ; this is a comment!<br>
* var1 = foo<br>
* var2 = bar<br>
- *<br>
+ * <br>
* [section2]<br>
* var1 = doo<br>
* </code>
- *
* <p>
* The format of ini files is fairly straight forward and is composed of three
* components:<br>
* <ul>
- * <li><b>Sections:</b> Ini files are split into sections, each section
- * starting with a section declaration. A section declaration starts with a '['
- * and ends with a ']'. Sections occur on one line only.</li>
- * <li><b>Parameters:</b> Items in a section are known as parameters.
- * Parameters have a typical <code>key = value</code> format.</li>
- * <li><b>Comments:</b> Lines starting with a ';' are assumed to be comments.
- * </li>
+ * <li><b>Sections:</b> Ini files are split into sections, each section starting
+ * with a section declaration. A section declaration starts with a '[' and ends
+ * with a ']'. Sections occur on one line only.</li>
+ * <li><b>Parameters:</b> Items in a section are known as parameters. Parameters
+ * have a typical <code>key = value</code> format.</li>
+ * <li><b>Comments:</b> Lines starting with a ';' are assumed to be comments.</li>
* </ul>
* </p>
- *
* <p>
* There are various implementations of the ini file format by various vendors
* which has caused a number of differences to appear. As far as possible this
* configuration tries to be lenient and support most of the differences.
* </p>
- *
* <p>
* Some of the differences supported are as follows:
* <ul>
* <li><b>Comments:</b> The '#' character is also accepted as a comment
* signifier.</li>
- * <li><b>Key value separtor:</b> The ':' character is also accepted in place
- * of '=' to separate keys and values in parameters, for example
+ * <li><b>Key value separtor:</b> The ':' character is also accepted in place of
+ * '=' to separate keys and values in parameters, for example
* <code>var1 : foo</code>.</li>
- * <li><b>Duplicate sections:</b> Typically duplicate sections are not allowed ,
+ * <li><b>Duplicate sections:</b> Typically duplicate sections are not allowed,
* this configuration does however support it. In the event of a duplicate
* section, the two section's values are merged.</li>
* <li><b>Duplicate parameters:</b> Typically duplicate parameters are only
* allowed if they are in two different sections, thus they are local to
* sections; this configuration simply merges duplicates; if a section has a
- * duplicate parameter the values are then added to the key as a list. </li>
+ * duplicate parameter the values are then added to the key as a list.</li>
* </ul>
* </p>
* <p>
@@ -102,13 +104,12 @@
* <p>
* In all instances, a parameter's key is prepended with its section name and a
* '.' (period). Thus a parameter named "var1" in "section1" will have the key
- * <code>section1.var1</code> in this configuration. Thus, a section's
- * parameters can easily be retrieved using the <code>subset</code> method
- * using the section name as the prefix.
+ * <code>section1.var1</code> in this configuration. (This is the default
+ * behavior. Because this is a hierarchical configuration you can change this by
+ * setting a different {@link ExpressionEngine}.)
* </p>
* <p>
- * <h3>Implementation Details:</h3>
- * Consider the following ini file:<br>
+ * <h3>Implementation Details:</h3> Consider the following ini file:<br>
* <code>
* default = ok<br>
* <br>
@@ -142,32 +143,47 @@
* is accessed simply using <code>getProperty("default")</code>.</li>
* <li>Section 1's parameters can be accessed using
* <code>getProperty("section1.var1")</code>.</li>
- * <li>The parameter named "bad" simply adds the parameter with an empty value.
- * </li>
- * <li>The empty key with value "= worse" is added using an empty key. This key
- * is still added to section 2 and the value can be accessed using
- * <code>getProperty("section2.")</code>, notice the period '.' following the
- * section name.</li>
+ * <li>The parameter named "bad" simply adds the parameter with an empty value.</li>
+ * <li>The empty key with value "= worse" is added using a key consisting of a
+ * single space character. This key is still added to section 2 and the value
+ * can be accessed using <code>getProperty("section2. ")</code>, notice the
+ * period '.' and the space following the section name.</li>
* <li>Section three uses both '=' and ':' to separate keys and values.</li>
* <li>Section 3 has a duplicate key named "var5". The value for this key is
* [test1, test2], and is represented as a List.</li>
* </ul>
* </p>
* <p>
+ * Internally, this configuration maps the content of the represented ini file
+ * to its node structure in the following way:
+ * <ul>
+ * <li>Sections are represented by direct child nodes of the root node.</li>
+ * <li>For the content of a section, corresponding nodes are created as children
+ * of the section node.</li>
+ * </ul>
+ * This explains how the keys for the properties can be constructed. You can
+ * also use other methods of {@link InMemoryConfiguration} for querying or
+ * manipulating the hierarchy of configuration nodes, for instance the
+ * {@code configurationAt()} method for obtaining the data of a specific
+ * section.
+ * </p>
+ * <p>
* The set of sections in this configuration can be retrieved using the
- * <code>getSections</code> method.
+ * {@code getSections()} method. For obtaining a {@link SubConfiguration}
+ * with the content of a specific section the {@code getSection()} method can be used.
* </p>
* <p>
- * <em>Note:</em> Configuration objects of this type can be read concurrently
- * by multiple threads. However if one of these threads modifies the object,
+ * <em>Note:</em> Configuration objects of this type can be read concurrently by
+ * multiple threads. However if one of these threads modifies the object,
* synchronization has to be performed manually.
* </p>
*
- * @author Trevor Miller
+ * @author <a
+ * href="http://commons.apache.org/configuration/team-list.html">Commons
+ * Configuration team</a>
* @version $Id$
- * @since 1.4
*/
-public class INIConfiguration extends AbstractFileConfiguration
+public class INIConfiguration extends AbstractHierarchicalFileConfiguration
{
/**
* The characters that signal the start of a comment line.
@@ -225,19 +241,22 @@
*
* @param writer - The writer to save the configuration to.
* @throws ConfigurationException If an error occurs while writing the
- * configuration
+ * configuration
*/
public void save(Writer writer) throws ConfigurationException
{
PrintWriter out = new PrintWriter(writer);
for (String section : getSections())
{
- out.print("[");
- out.print(section);
- out.print("]");
- out.println();
+ if (section != null)
+ {
+ out.print("[");
+ out.print(section);
+ out.print("]");
+ out.println();
+ }
- Configuration subset = subset(section);
+ Configuration subset = getSection(section);
Iterator<String> keys = subset.getKeys();
while (keys.hasNext())
{
@@ -245,11 +264,11 @@
Object value = subset.getProperty(key);
if (value instanceof Collection)
{
- for (Object val : (Collection) value)
+ for (Object v : (Collection<?>) value)
{
out.print(key);
out.print(" = ");
- out.print(formatValue(val.toString()));
+ out.print(formatValue(String.valueOf(v)));
out.println();
}
}
@@ -270,20 +289,21 @@
/**
* Load the configuration from the given reader. Note that the
- * <code>clear</code> method is not called so the configuration read in
- * will be merged with the current configuration.
+ * <code>clear</code> method is not called so the configuration read in will
+ * be merged with the current configuration.
*
* @param reader The reader to read the configuration from.
* @throws ConfigurationException If an error occurs while reading the
- * configuration
+ * configuration
*/
public void load(Reader reader) throws ConfigurationException
{
try
{
BufferedReader bufferedReader = new BufferedReader(reader);
+ ConfigurationNode sectionNode = getRootNode();
+
String line = bufferedReader.readLine();
- String section = "";
while (line != null)
{
line = line.trim();
@@ -291,8 +311,10 @@
{
if (isSectionLine(line))
{
- section = line.substring(1, line.length() - 1) + ".";
+ String section = line.substring(1, line.length() - 1);
+ sectionNode = getSectionNode(section);
}
+
else
{
String key = "";
@@ -300,7 +322,7 @@
int index = line.indexOf("=");
if (index >= 0)
{
- key = section + line.substring(0, index);
+ key = line.substring(0, index);
value = parseValue(line.substring(index + 1));
}
else
@@ -308,33 +330,44 @@
index = line.indexOf(":");
if (index >= 0)
{
- key = section + line.substring(0, index);
+ key = line.substring(0, index);
value = parseValue(line.substring(index + 1));
}
else
{
- key = section + line;
+ key = line;
}
}
- addProperty(key.trim(), value);
+ key = key.trim();
+ if (key.length() < 1)
+ {
+ // use space for properties with no key
+ key = " ";
+ }
+ createNode(sectionNode, key, value);
}
}
+
line = bufferedReader.readLine();
}
}
catch (IOException e)
{
- throw new ConfigurationException("Unable to load the configuration", e);
+ throw new ConfigurationException(
+ "Unable to load the configuration", e);
}
}
/**
- * Parse the value to remove the quotes and ignoring the comment.
- * Example:
+ * Parse the value to remove the quotes and ignoring the comment. Example:
*
- * <pre>"value" ; comment -> value</pre>
+ * <pre>
+ * "value" ; comment -> value
+ * </pre>
*
- * <pre>'value' ; comment -> value</pre>
+ * <pre>
+ * 'value' ; comment -> value
+ * </pre>
*
* @param value
*/
@@ -435,7 +468,7 @@
*
* @param line The line to check.
* @return true if the line is empty or starts with one of the comment
- * characters
+ * characters
*/
protected boolean isCommentLine(String line)
{
@@ -470,19 +503,135 @@
*/
public Set<String> getSections()
{
- Set<String> sections = new TreeSet<String>();
+ Set<String> sections = new LinkedHashSet<String>();
+ boolean globalSection = false;
- Iterator<String> keys = getKeys();
- while (keys.hasNext())
+ for (ConfigurationNode node : getRootNode().getChildren())
{
- String key = (String) keys.next();
- int index = key.indexOf(".");
- if (index >= 0)
+ if (isSectionNode(node))
{
- sections.add(key.substring(0, index));
+ if (globalSection)
+ {
+ sections.add(null);
+ globalSection = false;
+ }
+ sections.add(node.getName());
+ }
+ else
+ {
+ globalSection = true;
}
}
return sections;
}
+
+ /**
+ * Returns a configuration with the content of the specified section. This
+ * provides an easy way of working with a single section only. The way this
+ * configuration is structured internally, this method is very similar to
+ * calling
+ * <code>{@link HierarchicalConfiguration#configurationAt(String)}</code>
+ * with the name of the section in question. There are the following
+ * differences however:
+ * <ul>
+ * <li>This method never throws an exception. If the section does not exist,
+ * an empty configuration is returned.</li>
+ * <li>There is special support for the global section: Passing in
+ * <b>null</b> as section name returns a configuration with the content of
+ * the global section (which may also be empty).</li>
+ * </ul>
+ *
+ * @param name the name of the section in question; <b>null</b> represents
+ * the global section
+ * @return a configuration containing only the properties of the specified
+ * section
+ */
+ public SubConfiguration<ConfigurationNode> getSection(String name)
+ {
+ if (name == null)
+ {
+ return getGlobalSection();
+ }
+
+ else
+ {
+ try
+ {
+ return configurationAt(name);
+ }
+ catch (IllegalArgumentException iex)
+ {
+ // the passed in key does not map to exactly one node
+ // return an empty configuration
+ return new SubConfiguration<ConfigurationNode>(this,
+ new DefaultConfigurationNode());
+ }
+ }
+ }
+
+ /**
+ * Obtains the node representing the specified section. This method is
+ * called while the configuration is loaded. If a node for this section
+ * already exists, it is returned. Otherwise a new node is created.
+ *
+ * @param sectionName the name of the section
+ * @return the node for this section
+ */
+ private ConfigurationNode getSectionNode(String sectionName)
+ {
+ List<ConfigurationNode> nodes = getRootNode().getChildren(sectionName);
+ if (!nodes.isEmpty())
+ {
+ return nodes.get(0);
+ }
+
+ ConfigurationNode node = createNode(getRootNode(), sectionName);
+ markSectionNode(node);
+ return node;
+ }
+
+ /**
+ * Creates a sub configuration for the global section of the represented INI
+ * configuration.
+ *
+ * @return the sub configuration for the global section
+ */
+ private SubConfiguration<ConfigurationNode> getGlobalSection()
+ {
+ ViewNode parent = new ViewNode();
+
+ for (ConfigurationNode node : getRootNode().getChildren())
+ {
+ if (!isSectionNode(node))
+ {
+ parent.addChild(node);
+ }
+ }
+
+ return createSubnodeConfiguration(parent);
+ }
+
+ /**
+ * Marks a configuration node as a section node. This means that this node
+ * represents a section header. This implementation uses the node's
+ * reference property to store a flag.
+ *
+ * @param node the node to be marked
+ */
+ private static void markSectionNode(ConfigurationNode node)
+ {
+ node.setReference(Boolean.TRUE);
+ }
+
+ /**
+ * Checks whether the specified configuration node represents a section.
+ *
+ * @param node the node in question
+ * @return a flag whether this node represents a section
+ */
+ private static boolean isSectionNode(ConfigurationNode node)
+ {
+ return node.getReference() != null || node.getChildrenCount() > 0;
+ }
}
Modified: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestINIConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestINIConfiguration.java?rev=718717&r1=718716&r2=718717&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestINIConfiguration.java (original)
+++ commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestINIConfiguration.java Tue Nov 18 13:29:32 2008
@@ -5,9 +5,9 @@
* 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
@@ -17,23 +17,27 @@
package org.apache.commons.configuration2;
+import java.io.File;
+import java.io.FileWriter;
import java.io.IOException;
-import java.io.Reader;
+import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.Set;
-import org.apache.commons.configuration2.ConfigurationException;
-import org.apache.commons.configuration2.INIConfiguration;
-
import junit.framework.TestCase;
+import org.apache.commons.configuration2.tree.ConfigurationNode;
+
/**
* Test class for INIConfiguration.
*
- * @author Trevor Miller
+ * @author <a
+ * href="http://commons.apache.org/configuration/team-list.html">Commons
+ * Configuration team</a>
* @version $Id$
*/
public class TestINIConfiguration extends TestCase
@@ -41,30 +45,78 @@
private static String LINE_SEPARATOR = System.getProperty("line.separator");
/** Constant for the content of an ini file. */
- private static final String INI_DATA =
- "[section1]" + LINE_SEPARATOR
- + "var1 = foo" + LINE_SEPARATOR
- + "var2 = 451" + LINE_SEPARATOR
- + LINE_SEPARATOR
- + "[section2]" + LINE_SEPARATOR
- + "var1 = 123.45" + LINE_SEPARATOR
- + "var2 = bar" + LINE_SEPARATOR
- + LINE_SEPARATOR
- + "[section3]" + LINE_SEPARATOR
- + "var1 = true" + LINE_SEPARATOR
+ private static final String INI_DATA = "[section1]" + LINE_SEPARATOR
+ + "var1 = foo" + LINE_SEPARATOR + "var2 = 451" + LINE_SEPARATOR
+ + LINE_SEPARATOR + "[section2]" + LINE_SEPARATOR + "var1 = 123.45"
+ + LINE_SEPARATOR + "var2 = bar" + LINE_SEPARATOR + LINE_SEPARATOR
+ + "[section3]" + LINE_SEPARATOR + "var1 = true" + LINE_SEPARATOR
+ "interpolated = ${section3.var1}" + LINE_SEPARATOR
- + "multi = foo" + LINE_SEPARATOR
- + "multi = bar" + LINE_SEPARATOR
+ + "multi = foo" + LINE_SEPARATOR + "multi = bar" + LINE_SEPARATOR
+ LINE_SEPARATOR;
- private static final String INI_DATA2 =
- "[section4]" + LINE_SEPARATOR
+ private static final String INI_DATA2 = "[section4]" + LINE_SEPARATOR
+ "var1 = \"quoted value\"" + LINE_SEPARATOR
+ "var2 = \"quoted value\\nwith \\\"quotes\\\"\"" + LINE_SEPARATOR
+ "var3 = 123 ; comment" + LINE_SEPARATOR
+ "var4 = \"1;2;3\" ; comment" + LINE_SEPARATOR
+ "var5 = '\\'quoted\\' \"value\"' ; comment";
+ /** An ini file with a global section. */
+ private static final String INI_DATA_GLOBAL = "globalVar = testGlobal"
+ + LINE_SEPARATOR + LINE_SEPARATOR + INI_DATA;
+
+ /** A test ini file. */
+ private static final File TEST_FILE = ConfigurationAssert.getOutFile("test.ini");
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ if (TEST_FILE.exists())
+ {
+ assertTrue("Cannot remove test file: " + TEST_FILE, TEST_FILE
+ .delete());
+ }
+
+ super.tearDown();
+ }
+
+ /**
+ * Creates a INIConfiguration object that is initialized from
+ * the given data.
+ *
+ * @param data the data of the configuration (an ini file as string)
+ * @return the initialized configuration
+ * @throws ConfigurationException if an error occurs
+ */
+ private static INIConfiguration setUpConfig(String data)
+ throws ConfigurationException
+ {
+ StringReader reader = new StringReader(data);
+ INIConfiguration instance = new INIConfiguration();
+ instance.load(reader);
+ reader.close();
+ return instance;
+ }
+
+ /**
+ * Writes a test ini file.
+ *
+ * @param content the content of the file
+ * @throws IOException if an error occurs
+ */
+ private static void writeTestFile(String content) throws IOException
+ {
+ PrintWriter out = new PrintWriter(new FileWriter(TEST_FILE));
+ try
+ {
+ out.println(content);
+ }
+ finally
+ {
+ out.close();
+ }
+ }
+
/**
* Test of save method, of class {@link INIConfiguration}.
*/
@@ -86,6 +138,18 @@
}
/**
+ * Tests saving a configuration that contains a global section.
+ */
+ public void testSaveWithGlobalSection() throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
+ StringWriter writer = new StringWriter();
+ config.save(writer);
+ assertEquals("Wrong content of ini file", INI_DATA_GLOBAL, writer
+ .toString());
+ }
+
+ /**
* Test of load method, of class {@link INIConfiguration}.
*/
public void testLoad() throws Exception
@@ -103,17 +167,46 @@
}
/**
- * Helper method for testing the load operation. Loads the specified content
- * into a configuration and then checks some properties.
+ * Tests loading a configuration from a File.
+ */
+ public void testLoadFile() throws ConfigurationException, IOException
+ {
+ writeTestFile(INI_DATA);
+ INIConfiguration config = new INIConfiguration(
+ TEST_FILE);
+ checkContent(config);
+ }
+
+ /**
+ * Tests loading a configuration from a file name.
+ */
+ public void testLoadFileName() throws ConfigurationException, IOException
+ {
+ writeTestFile(INI_DATA);
+ INIConfiguration config = new INIConfiguration(
+ TEST_FILE.getAbsolutePath());
+ checkContent(config);
+ }
+
+ /**
+ * Tests loading a configuration from a URL.
+ */
+ public void testLoadURL() throws ConfigurationException, IOException
+ {
+ writeTestFile(INI_DATA);
+ INIConfiguration config = new INIConfiguration(
+ TEST_FILE.toURI().toURL());
+ checkContent(config);
+ }
+
+ /**
+ * Tests the values of some properties to ensure that the configuration was
+ * correctly loaded.
*
- * @param data the data to load
+ * @param instance the configuration to check
*/
- private void checkLoad(String data) throws ConfigurationException, IOException
+ private void checkContent(INIConfiguration instance)
{
- Reader reader = new StringReader(data);
- INIConfiguration instance = new INIConfiguration();
- instance.load(reader);
- reader.close();
assertTrue(instance.getString("section1.var1").equals("foo"));
assertTrue(instance.getInt("section1.var2") == 451);
assertTrue(instance.getDouble("section2.var1") == 123.45);
@@ -123,7 +216,20 @@
}
/**
- * Test of isCommentLine method, of class {@link INIConfiguration}.
+ * Helper method for testing the load operation. Loads the specified content
+ * into a configuration and then checks some properties.
+ *
+ * @param data the data to load
+ */
+ private void checkLoad(String data) throws ConfigurationException
+ {
+ INIConfiguration instance = setUpConfig(data);
+ checkContent(instance);
+ }
+
+ /**
+ * Test of isCommentLine method, of class
+ * {@link INIConfiguration}.
*/
public void testIsCommentLine()
{
@@ -135,7 +241,8 @@
}
/**
- * Test of isSectionLine method, of class {@link INIConfiguration}.
+ * Test of isSectionLine method, of class
+ * {@link INIConfiguration}.
*/
public void testIsSectionLine()
{
@@ -146,7 +253,8 @@
}
/**
- * Test of getSections method, of class {@link INIConfiguration}.
+ * Test of getSections method, of class {@link INIConfiguration}
+ * .
*/
public void testGetSections()
{
@@ -157,47 +265,39 @@
expResult.add("test1");
expResult.add("test2");
Set<String> result = instance.getSections();
- assertEquals(expResult, result);
+ assertEquals("Wrong set with sections", expResult, result);
}
public void testQuotedValue() throws Exception
{
- INIConfiguration config = new INIConfiguration();
- config.load(new StringReader(INI_DATA2));
-
+ INIConfiguration config = setUpConfig(INI_DATA2);
assertEquals("value", "quoted value", config.getString("section4.var1"));
}
public void testQuotedValueWithQuotes() throws Exception
{
- INIConfiguration config = new INIConfiguration();
- config.load(new StringReader(INI_DATA2));
-
- assertEquals("value", "quoted value\\nwith \"quotes\"", config.getString("section4.var2"));
+ INIConfiguration config = setUpConfig(INI_DATA2);
+ assertEquals("value", "quoted value\\nwith \"quotes\"", config
+ .getString("section4.var2"));
}
public void testValueWithComment() throws Exception
{
- INIConfiguration config = new INIConfiguration();
- config.load(new StringReader(INI_DATA2));
-
+ INIConfiguration config = setUpConfig(INI_DATA2);
assertEquals("value", "123", config.getString("section4.var3"));
}
public void testQuotedValueWithComment() throws Exception
{
- INIConfiguration config = new INIConfiguration();
- config.load(new StringReader(INI_DATA2));
-
+ INIConfiguration config = setUpConfig(INI_DATA2);
assertEquals("value", "1;2;3", config.getString("section4.var4"));
}
public void testQuotedValueWithSingleQuotes() throws Exception
{
- INIConfiguration config = new INIConfiguration();
- config.load(new StringReader(INI_DATA2));
-
- assertEquals("value", "'quoted' \"value\"", config.getString("section4.var5"));
+ INIConfiguration config = setUpConfig(INI_DATA2);
+ assertEquals("value", "'quoted' \"value\"", config
+ .getString("section4.var5"));
}
public void testWriteValueWithCommentChar() throws Exception
@@ -237,4 +337,177 @@
assertEquals("Wrong propert value", " [test@cmd ~]$ ", config
.getString("CmdPrompt"));
}
+
+ /**
+ * Tests a property that has no value.
+ */
+ public void testGetPropertyNoValue() throws ConfigurationException
+ {
+ final String data = INI_DATA2 + LINE_SEPARATOR + "noValue ="
+ + LINE_SEPARATOR;
+ INIConfiguration config = setUpConfig(data);
+ assertEquals("Wrong value of key", "", config
+ .getString("section4.noValue"));
+ }
+
+ /**
+ * Tests a property that has no key.
+ */
+ public void testGetPropertyNoKey() throws ConfigurationException
+ {
+ final String data = INI_DATA2 + LINE_SEPARATOR + "= noKey"
+ + LINE_SEPARATOR;
+ INIConfiguration config = setUpConfig(data);
+ assertEquals("Cannot find property with no key", "noKey", config
+ .getString("section4. "));
+ }
+
+ /**
+ * Tests reading a property from the global section.
+ */
+ public void testGlobalProperty() throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
+ assertEquals("Wrong value of global property", "testGlobal", config
+ .getString("globalVar"));
+ }
+
+ /**
+ * Tests whether the specified configuration contains exactly the expected
+ * sections.
+ *
+ * @param config the configuration to check
+ * @param expected an array with the expected sections
+ */
+ private void checkSectionNames(INIConfiguration config,
+ String[] expected)
+ {
+ Set<String> sectionNames = config.getSections();
+ Iterator<String> it = sectionNames.iterator();
+ for (int idx = 0; idx < expected.length; idx++)
+ {
+ assertEquals("Wrong section at " + idx, expected[idx], it.next());
+ }
+ assertFalse("Too many sections", it.hasNext());
+ }
+
+ /**
+ * Tests the names of the sections returned by the configuration.
+ *
+ * @param data the data of the ini configuration
+ * @param expected the expected section names
+ * @return the configuration instance
+ */
+ private INIConfiguration checkSectionNames(String data,
+ String[] expected) throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(data);
+ checkSectionNames(config, expected);
+ return config;
+ }
+
+ /**
+ * Tests querying the sections if a global section if available.
+ */
+ public void testGetSectionsWithGlobal() throws ConfigurationException
+ {
+ checkSectionNames(INI_DATA_GLOBAL, new String[] {
+ null, "section1", "section2", "section3"
+ });
+ }
+
+ /**
+ * Tests querying the sections if there is no global section.
+ */
+ public void testGetSectionsNoGlobal() throws ConfigurationException
+ {
+ checkSectionNames(INI_DATA, new String[] {
+ "section1", "section2", "section3"
+ });
+ }
+
+ /**
+ * Tests whether variables containing a dot are not misinterpreted as
+ * sections. This test is related to CONFIGURATION-327.
+ */
+ public void testGetSectionsDottedVar() throws ConfigurationException
+ {
+ final String data = "dotted.var = 1" + LINE_SEPARATOR + INI_DATA_GLOBAL;
+ INIConfiguration config = checkSectionNames(data,
+ new String[] {
+ null, "section1", "section2", "section3"
+ });
+ assertEquals("Wrong value of dotted variable", 1, config
+ .getInt("dotted..var"));
+ }
+
+ /**
+ * Tests whether a section added later is also found by getSections().
+ */
+ public void testGetSectionsAdded() throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(INI_DATA2);
+ config.addProperty("section5.test", Boolean.TRUE);
+ checkSectionNames(config, new String[] {
+ "section4", "section5"
+ });
+ }
+
+ /**
+ * Tests querying the properties of an existing section.
+ */
+ public void testGetSectionExisting() throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(INI_DATA);
+ SubConfiguration<ConfigurationNode> section = config.getSection("section1");
+ assertEquals("Wrong value of var1", "foo", section.getString("var1"));
+ assertEquals("Wrong value of var2", "451", section.getString("var2"));
+ }
+
+ /**
+ * Tests querying the properties of a section that was merged from two
+ * sections with the same name.
+ */
+ public void testGetSectionMerged() throws ConfigurationException
+ {
+ final String data = INI_DATA + "[section1]" + LINE_SEPARATOR
+ + "var3 = merged" + LINE_SEPARATOR;
+ INIConfiguration config = setUpConfig(data);
+ SubConfiguration<ConfigurationNode> section = config.getSection("section1");
+ assertEquals("Wrong value of var1", "foo", section.getString("var1"));
+ assertEquals("Wrong value of var2", "451", section.getString("var2"));
+ assertEquals("Wrong value of var3", "merged", section.getString("var3"));
+ }
+
+ /**
+ * Tests querying the content of the global section.
+ */
+ public void testGetSectionGlobal() throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
+ SubConfiguration<ConfigurationNode> section = config.getSection(null);
+ assertEquals("Wrong value of global variable", "testGlobal", section
+ .getString("globalVar"));
+ }
+
+ /**
+ * Tests querying the content of the global section if there is none.
+ */
+ public void testGetSectionGlobalNonExisting() throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(INI_DATA);
+ SubConfiguration<ConfigurationNode> section = config.getSection(null);
+ assertTrue("Sub config not empty", section.isEmpty());
+ }
+
+ /**
+ * Tests querying a non existing section.
+ */
+ public void testGetSectionNonExisting() throws ConfigurationException
+ {
+ INIConfiguration config = setUpConfig(INI_DATA);
+ SubConfiguration<ConfigurationNode> section = config
+ .getSection("Non existing section");
+ assertTrue("Sub config not empty", section.isEmpty());
+ }
}
Modified: commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml?rev=718717&r1=718716&r2=718717&view=diff
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml (original)
+++ commons/proper/configuration/branches/configuration2_experimental/xdocs/changes.xml Tue Nov 18 13:29:32 2008
@@ -124,14 +124,36 @@
method can be overridden by derived classes for injecting custom
BeanDeclaration implementations.
</action>
- <action dev="oheger" type="fix" issue="CONFIGURATION-307">
- XMLConfiguration now supports the xml:space attribute. This attribute
- can be used to preserve whitespace in the content of XML elements.
- </action>
<action dev="oheger" type="fix" issue="CONFIGURATION-328">
A bug in XMLConfiguration.addNodes() made it impossible to add
attribute nodes using this method. This has been fixed.
</action>
+ <action dev="oheger" type="fix" issue="CONFIGURATION-327">
+ INIConfiguration misinterpreted variables in the global section with
+ a dot in their name as section names. HierarchicalINIConfiguration fixes
+ this problem.
+ </action>
+ <action dev="oheger" type="add" issue="CONFIGURATION-326">
+ INIConfiguration does not support obtaining a subset for the global
+ section. HierarchicalINIConfiguration provides the getSection() method
+ that returns the content of the global section if null is passed in as
+ section name.
+ </action>
+ <action dev="oheger" type="fix" issue="CONFIGURATION-325">
+ INIConfiguration does not return the global section in its getSections()
+ method. HierarchicalINIConfiguration fixes this problem.
+ </action>
+ <action dev="oheger" type="update">
+ INIConfiguration has been deprecated. Its functionality is now available
+ through the new HierarchicalINIConfiguration class.
+ </action>
+ <action dev="oheger" type="add">
+ With HierarchicalINIConfiguration a complete new Configuration
+ implementation for parsing Windows INI files is available. This new
+ class is a full replacement of INIConfiguration and addresses some of its
+ shortcomings. Being derived from HierarchicalConfiguration it offers
+ the enhanced functionality of hierarchical configurations.
+ </action>
<action dev="ebourg" type="fix" issue="CONFIGURATION-322">
ConfigurationDynaBean now works properly with indexed properties
stored internally in the underlying configuration as arrays.
@@ -152,6 +174,10 @@
for each modified child configuration. Now this event is sent only
once after the affected child configuration was updated.
</action>
+ <action dev="oheger" type="fix" issue="CONFIGURATION-307">
+ XMLConfiguration now supports the xml:space attribute. This attribute
+ can be used to preserve whitespace in the content of XML elements.
+ </action>
<action dev="oheger" type="fix" issue="CONFIGURATION-306">
INIConfiguration now preserves whitespace in quoted values.
</action>