Return-Path:
+ * A specialized hierarchical configuration implementation for parsing ini
+ * files.
+ *
* An initialization or ini file is a configuration file typically found on
* Microsoft's Windows operating system and contains data for Windows based
* applications.
*
* 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.
*
* A typcial ini file could look something like:
*
* The format of ini files is fairly straight forward and is composed of three
* components:
* var1 = foo
* var2 = bar
- *
+ *
* [section2]
* var1 = doo
*
- *
*
*
- *
* key = value
format.key = value
format.
* 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. *
- * ** Some of the differences supported are as follows: *
var1 : foo
.@@ -102,13 +104,12 @@ *
* 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
- * section1.var1
in this configuration. Thus, a section's
- * parameters can easily be retrieved using the subset
method
- * using the section name as the prefix.
+ * section1.var1
in this configuration. (This is the default
+ * behavior. Because this is a hierarchical configuration you can change this by
+ * setting a different {@link ExpressionEngine}.)
*
- *
* default = ok
*
@@ -142,34 +143,57 @@
* is accessed simply using getProperty("default")
.
* Section 1's parameters can be accessed using
* getProperty("section1.var1")
.
- * The parameter named "bad" simply adds the parameter with an empty value.
- *
- * 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
- * getProperty("section2.")
, notice the period '.' following the
- * section name.
+ * The parameter named "bad" simply adds the parameter with an empty value.
+ * 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 getProperty("section2. ")
, notice the
+ * period '.' and the space following the section name.
* Section three uses both '=' and ':' to separate keys and values.
* Section 3 has a duplicate key named "var5". The value for this key is
* [test1, test2], and is represented as a List.
*
*
*
+ * Internally, this configuration maps the content of the represented ini file
+ * to its node structure in the following way:
+ *
+ * - Sections are represented by direct child nodes of the root node.
+ * - For the content of a section, corresponding nodes are created as children
+ * of the section node.
+ *
+ * This explains how the keys for the properties can be constructed. You can
+ * also use other methods of {@link HierarchicalConfiguration} for querying or
+ * manipulating the hierarchy of configuration nodes, for instance the
+ * configurationAt()
method for obtaining the data of a specific
+ * section.
+ *
+ *
* The set of sections in this configuration can be retrieved using the
- * getSections
method.
+ * getSections()
method. For obtaining a
+ * SubnodeConfiguration
with the content of a specific section the
+ * getSection()
method can be used.
*
*
- * Note: Configuration objects of this type can be read concurrently
- * by multiple threads. However if one of these threads modifies the object,
+ * Note: 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.
*
*
- * @author Trevor Miller
+ * @author Commons
+ * Configuration team
* @version $Id$
- * @since 1.4
+ * @since 1.6
*/
-public class INIConfiguration extends AbstractFileConfiguration
+public class HierarchicalINIConfiguration extends
+ AbstractHierarchicalFileConfiguration
{
/**
+ * The serial version UID.
+ */
+ private static final long serialVersionUID = 2548006161386850670L;
+
+ /**
* The characters that signal the start of a comment line.
*/
protected static final String COMMENT_CHARS = "#;";
@@ -182,7 +206,7 @@
/**
* Create a new empty INI Configuration.
*/
- public INIConfiguration()
+ public HierarchicalINIConfiguration()
{
super();
}
@@ -193,7 +217,8 @@
* @param filename The name pr path of the ini file to load.
* @throws ConfigurationException If an error occurs while loading the file
*/
- public INIConfiguration(String filename) throws ConfigurationException
+ public HierarchicalINIConfiguration(String filename)
+ throws ConfigurationException
{
super(filename);
}
@@ -204,7 +229,8 @@
* @param file The ini file to load.
* @throws ConfigurationException If an error occurs while loading the file
*/
- public INIConfiguration(File file) throws ConfigurationException
+ public HierarchicalINIConfiguration(File file)
+ throws ConfigurationException
{
super(file);
}
@@ -215,7 +241,7 @@
* @param url The url of the ini file to load.
* @throws ConfigurationException If an error occurs while loading the file
*/
- public INIConfiguration(URL url) throws ConfigurationException
+ public HierarchicalINIConfiguration(URL url) throws ConfigurationException
{
super(url);
}
@@ -225,7 +251,7 @@
*
* @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
{
@@ -234,12 +260,15 @@
while (it.hasNext())
{
String section = (String) it.next();
- 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 keys = subset.getKeys();
while (keys.hasNext())
{
@@ -274,20 +303,21 @@
/**
* Load the configuration from the given reader. Note that the
- * clear
method is not called so the configuration read in
- * will be merged with the current configuration.
+ * clear
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();
@@ -295,8 +325,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 = "";
@@ -304,7 +336,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
@@ -312,33 +344,46 @@
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 = " ";
+ }
+ ConfigurationNode node = createNode(key);
+ node.setValue(value);
+ sectionNode.addChild(node);
}
}
+
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:
*
- * "value" ; comment -> value
+ *
+ * "value" ; comment -> value
+ *
*
- * 'value' ; comment -> value
+ *
+ * 'value' ; comment -> value
+ *
*
* @param value
*/
@@ -401,7 +446,7 @@
}
String v = result.toString();
- if(!quoted)
+ if (!quoted)
{
v = v.trim();
}
@@ -439,7 +484,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)
{
@@ -474,19 +519,138 @@
*/
public Set getSections()
{
- Set sections = new TreeSet();
+ Set sections = new ListOrderedSet();
+ boolean globalSection = false;
- Iterator keys = getKeys();
- while (keys.hasNext())
+ for (Iterator it = getRootNode().getChildren().iterator(); it.hasNext();)
{
- String key = (String) keys.next();
- int index = key.indexOf(".");
- if (index >= 0)
+ ConfigurationNode node = (ConfigurationNode) it.next();
+ if (isSectionNode(node))
+ {
+ if (globalSection)
+ {
+ sections.add(null);
+ globalSection = false;
+ }
+ sections.add(node.getName());
+ }
+ else
{
- sections.add(key.substring(0, index));
+ 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
+ * {@link HierarchicalConfiguration#configurationAt(String)}
+ * with the name of the section in question. There are the following
+ * differences however:
+ *
+ * - This method never throws an exception. If the section does not exist,
+ * an empty configuration is returned.
+ * - There is special support for the global section: Passing in
+ * null as section name returns a configuration with the content of
+ * the global section (which may also be empty).
+ *
+ *
+ * @param name the name of the section in question; null represents
+ * the global section
+ * @return a configuration containing only the properties of the specified
+ * section
+ */
+ public SubnodeConfiguration 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 SubnodeConfiguration(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 nodes = getRootNode().getChildren(sectionName);
+ if (!nodes.isEmpty())
+ {
+ return (ConfigurationNode) nodes.get(0);
+ }
+
+ ConfigurationNode node = createNode(sectionName);
+ markSectionNode(node);
+ getRootNode().addChild(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 SubnodeConfiguration getGlobalSection()
+ {
+ ViewNode parent = new ViewNode();
+
+ for (Iterator it = getRootNode().getChildren().iterator(); it.hasNext();)
+ {
+ ConfigurationNode node = (ConfigurationNode) it.next();
+ 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;
+ }
}
Copied: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalINIConfiguration.java (from r701469, commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestINIConfiguration.java)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalINIConfiguration.java?p2=commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalINIConfiguration.java&p1=commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestINIConfiguration.java&r1=701469&r2=718120&rev=718120&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestINIConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalINIConfiguration.java Sun Nov 16 13:36:54 2008
@@ -17,195 +17,295 @@
package org.apache.commons.configuration;
+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 junit.framework.TestCase;
/**
- * Test class for INIConfiguration.
+ * Test class for HierarchicalINIConfiguration.
*
- * @author Trevor Miller
+ * @author Commons
+ * Configuration team
* @version $Id$
*/
-public class TestINIConfiguration extends TestCase
+public class TestHierarchicalINIConfiguration extends TestCase
{
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 = new File("target/test.ini");
+
+ protected void tearDown() throws Exception
+ {
+ if (TEST_FILE.exists())
+ {
+ assertTrue("Cannot remove test file: " + TEST_FILE, TEST_FILE
+ .delete());
+ }
+
+ super.tearDown();
+ }
+
/**
- * Test of save method, of class {@link INIConfiguration}.
+ * Creates a HierarchicalINIConfiguration 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
*/
- public void testSave() throws Exception
- {
- Writer writer = new StringWriter();
- INIConfiguration instance = new INIConfiguration();
- instance.addProperty("section1.var1", "foo");
- instance.addProperty("section1.var2", "451");
- instance.addProperty("section2.var1", "123.45");
- instance.addProperty("section2.var2", "bar");
- instance.addProperty("section3.var1", "true");
- instance.addProperty("section3.interpolated", "${section3.var1}");
- instance.addProperty("section3.multi", "foo");
- instance.addProperty("section3.multi", "bar");
- instance.save(writer);
+ private static HierarchicalINIConfiguration setUpConfig(String data)
+ throws ConfigurationException
+ {
+ StringReader reader = new StringReader(data);
+ HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
+ 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 HierarchicalINIConfiguration}.
+ */
+ public void testSave() throws Exception
+ {
+ Writer writer = new StringWriter();
+ HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
+ instance.addProperty("section1.var1", "foo");
+ instance.addProperty("section1.var2", "451");
+ instance.addProperty("section2.var1", "123.45");
+ instance.addProperty("section2.var2", "bar");
+ instance.addProperty("section3.var1", "true");
+ instance.addProperty("section3.interpolated", "${section3.var1}");
+ instance.addProperty("section3.multi", "foo");
+ instance.addProperty("section3.multi", "bar");
+ instance.save(writer);
assertEquals("Wrong content of ini file", INI_DATA, writer.toString());
- }
+ }
- /**
- * Test of load method, of class {@link INIConfiguration}.
+ /**
+ * Tests saving a configuration that contains a global section.
*/
- public void testLoad() throws Exception
- {
- checkLoad(INI_DATA);
- }
+ public void testSaveWithGlobalSection() throws ConfigurationException
+ {
+ HierarchicalINIConfiguration 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 HierarchicalINIConfiguration}.
+ */
+ public void testLoad() throws Exception
+ {
+ checkLoad(INI_DATA);
+ }
+
+ /**
* Tests the load() method when the alternative value separator is used (a
* ':' for '=').
*/
- public void testLoadAlternativeSeparator() throws Exception
- {
- checkLoad(INI_DATA.replace('=', ':'));
- }
+ public void testLoadAlternativeSeparator() throws Exception
+ {
+ checkLoad(INI_DATA.replace('=', ':'));
+ }
+
+ /**
+ * Tests loading a configuration from a File.
+ */
+ public void testLoadFile() throws ConfigurationException, IOException
+ {
+ writeTestFile(INI_DATA);
+ HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
+ TEST_FILE);
+ checkContent(config);
+ }
- /**
+ /**
+ * Tests loading a configuration from a file name.
+ */
+ public void testLoadFileName() throws ConfigurationException, IOException
+ {
+ writeTestFile(INI_DATA);
+ HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
+ TEST_FILE.getAbsolutePath());
+ checkContent(config);
+ }
+
+ /**
+ * Tests loading a configuration from a URL.
+ */
+ public void testLoadURL() throws ConfigurationException, IOException
+ {
+ writeTestFile(INI_DATA);
+ HierarchicalINIConfiguration config = new HierarchicalINIConfiguration(
+ TEST_FILE.toURL());
+ checkContent(config);
+ }
+
+ /**
+ * Tests the values of some properties to ensure that the configuration was
+ * correctly loaded.
+ *
+ * @param instance the configuration to check
+ */
+ private void checkContent(HierarchicalINIConfiguration instance)
+ {
+ assertTrue(instance.getString("section1.var1").equals("foo"));
+ assertTrue(instance.getInt("section1.var2") == 451);
+ assertTrue(instance.getDouble("section2.var1") == 123.45);
+ assertTrue(instance.getString("section2.var2").equals("bar"));
+ assertTrue(instance.getBoolean("section3.var1"));
+ assertTrue(instance.getSections().size() == 3);
+ }
+
+ /**
* 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, IOException
- {
- 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);
- assertTrue(instance.getString("section2.var2").equals("bar"));
- assertTrue(instance.getBoolean("section3.var1"));
- assertTrue(instance.getSections().size() == 3);
- }
-
- /**
- * Test of isCommentLine method, of class {@link INIConfiguration}.
- */
- public void testIsCommentLine()
- {
- INIConfiguration instance = new INIConfiguration();
- assertTrue(instance.isCommentLine("#comment1"));
- assertTrue(instance.isCommentLine(";comment1"));
- assertFalse(instance.isCommentLine("nocomment=true"));
- assertFalse(instance.isCommentLine(null));
- }
-
- /**
- * Test of isSectionLine method, of class {@link INIConfiguration}.
- */
- public void testIsSectionLine()
- {
- INIConfiguration instance = new INIConfiguration();
- assertTrue(instance.isSectionLine("[section]"));
- assertFalse(instance.isSectionLine("nosection=true"));
- assertFalse(instance.isSectionLine(null));
- }
-
- /**
- * Test of getSections method, of class {@link INIConfiguration}.
- */
- public void testGetSections()
- {
- INIConfiguration instance = new INIConfiguration();
- instance.addProperty("test1.foo", "bar");
- instance.addProperty("test2.foo", "abc");
- Set expResult = new HashSet();
- expResult.add("test1");
- expResult.add("test2");
- Set result = instance.getSections();
- assertEquals(expResult, result);
- }
+ private void checkLoad(String data) throws ConfigurationException
+ {
+ HierarchicalINIConfiguration instance = setUpConfig(data);
+ checkContent(instance);
+ }
- public void testQuotedValue() throws Exception
+ /**
+ * Test of isCommentLine method, of class
+ * {@link HierarchicalINIConfiguration}.
+ */
+ public void testIsCommentLine()
+ {
+ HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
+ assertTrue(instance.isCommentLine("#comment1"));
+ assertTrue(instance.isCommentLine(";comment1"));
+ assertFalse(instance.isCommentLine("nocomment=true"));
+ assertFalse(instance.isCommentLine(null));
+ }
+
+ /**
+ * Test of isSectionLine method, of class
+ * {@link HierarchicalINIConfiguration}.
+ */
+ public void testIsSectionLine()
+ {
+ HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
+ assertTrue(instance.isSectionLine("[section]"));
+ assertFalse(instance.isSectionLine("nosection=true"));
+ assertFalse(instance.isSectionLine(null));
+ }
+
+ /**
+ * Test of getSections method, of class {@link HierarchicalINIConfiguration}
+ * .
+ */
+ public void testGetSections()
{
- INIConfiguration config = new INIConfiguration();
- config.load(new StringReader(INI_DATA2));
+ HierarchicalINIConfiguration instance = new HierarchicalINIConfiguration();
+ instance.addProperty("test1.foo", "bar");
+ instance.addProperty("test2.foo", "abc");
+ Set expResult = new HashSet();
+ expResult.add("test1");
+ expResult.add("test2");
+ Set result = instance.getSections();
+ assertEquals(expResult, result);
+ }
+ public void testQuotedValue() throws Exception
+ {
+ HierarchicalINIConfiguration 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"));
+ HierarchicalINIConfiguration 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));
-
+ HierarchicalINIConfiguration 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));
-
+ HierarchicalINIConfiguration 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"));
+ HierarchicalINIConfiguration config = setUpConfig(INI_DATA2);
+ assertEquals("value", "'quoted' \"value\"", config
+ .getString("section4.var5"));
}
public void testWriteValueWithCommentChar() throws Exception
{
- INIConfiguration config = new INIConfiguration();
+ HierarchicalINIConfiguration config = new HierarchicalINIConfiguration();
config.setProperty("section.key1", "1;2;3");
StringWriter writer = new StringWriter();
config.save(writer);
- INIConfiguration config2 = new INIConfiguration();
+ HierarchicalINIConfiguration config2 = new HierarchicalINIConfiguration();
config2.load(new StringReader(writer.toString()));
assertEquals("value", "1;2;3", config2.getString("section.key1"));
@@ -217,7 +317,7 @@
public void testQuotedValueWithWhitespace() throws Exception
{
final String content = "CmdPrompt = \" [test@cmd ~]$ \"";
- INIConfiguration config = new INIConfiguration();
+ HierarchicalINIConfiguration config = new HierarchicalINIConfiguration();
config.load(new StringReader(content));
assertEquals("Wrong propert value", " [test@cmd ~]$ ", config
.getString("CmdPrompt"));
@@ -229,9 +329,182 @@
public void testQuotedValueWithWhitespaceAndComment() throws Exception
{
final String content = "CmdPrompt = \" [test@cmd ~]$ \" ; a comment";
- INIConfiguration config = new INIConfiguration();
+ HierarchicalINIConfiguration config = new HierarchicalINIConfiguration();
config.load(new StringReader(content));
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;
+ HierarchicalINIConfiguration 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;
+ HierarchicalINIConfiguration 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
+ {
+ HierarchicalINIConfiguration 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(HierarchicalINIConfiguration config,
+ String[] expected)
+ {
+ Set sectionNames = config.getSections();
+ Iterator 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 HierarchicalINIConfiguration checkSectionNames(String data,
+ String[] expected) throws ConfigurationException
+ {
+ HierarchicalINIConfiguration 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;
+ HierarchicalINIConfiguration 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
+ {
+ HierarchicalINIConfiguration 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
+ {
+ HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
+ SubnodeConfiguration 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;
+ HierarchicalINIConfiguration config = setUpConfig(data);
+ SubnodeConfiguration 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
+ {
+ HierarchicalINIConfiguration config = setUpConfig(INI_DATA_GLOBAL);
+ SubnodeConfiguration 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
+ {
+ HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
+ SubnodeConfiguration section = config.getSection(null);
+ assertTrue("Sub config not empty", section.isEmpty());
+ }
+
+ /**
+ * Tests querying a non existing section.
+ */
+ public void testGetSectionNonExisting() throws ConfigurationException
+ {
+ HierarchicalINIConfiguration config = setUpConfig(INI_DATA);
+ SubnodeConfiguration section = config
+ .getSection("Non existing section");
+ assertTrue("Sub config not empty", section.isEmpty());
+ }
}
Modified: commons/proper/configuration/trunk/xdocs/changes.xml
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/xdocs/changes.xml?rev=718120&r1=718119&r2=718120&view=diff
==============================================================================
--- commons/proper/configuration/trunk/xdocs/changes.xml (original)
+++ commons/proper/configuration/trunk/xdocs/changes.xml Sun Nov 16 13:36:54 2008
@@ -98,6 +98,13 @@
method can be overridden by derived classes for injecting custom
BeanDeclaration implementations.
+
+ 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.
+
XMLConfiguration now supports the xml:space attribute. This attribute
can be used to preserve whitespace in the content of XML elements.