commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject svn commit: r718120 - in /commons/proper/configuration/trunk: src/java/org/apache/commons/configuration/HierarchicalINIConfiguration.java src/test/org/apache/commons/configuration/TestHierarchicalINIConfiguration.java xdocs/changes.xml
Date Sun, 16 Nov 2008 21:36:55 GMT
Author: oheger
Date: Sun Nov 16 13:36:54 2008
New Revision: 718120

URL: http://svn.apache.org/viewvc?rev=718120&view=rev
Log:
Initial version of a new hierarchical Configuration implementation for parsing Windows INI
files.

Added:
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalINIConfiguration.java
      - copied, changed from r701469, commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/INIConfiguration.java
    commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestHierarchicalINIConfiguration.java
      - copied, changed from r701469, commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestINIConfiguration.java
Modified:
    commons/proper/configuration/trunk/xdocs/changes.xml

Copied: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalINIConfiguration.java
(from r701469, commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/INIConfiguration.java)
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalINIConfiguration.java?p2=commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalINIConfiguration.java&p1=commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/INIConfiguration.java&r1=701469&r2=718120&rev=718120&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/INIConfiguration.java
(original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/HierarchicalINIConfiguration.java
Sun Nov 16 13:36:54 2008
@@ -14,7 +14,6 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
 package org.apache.commons.configuration;
 
 import java.io.BufferedReader;
@@ -26,24 +25,31 @@
 import java.net.URL;
 import java.util.Collection;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Set;
-import java.util.TreeSet;
 
+import org.apache.commons.collections.set.ListOrderedSet;
+import org.apache.commons.configuration.tree.ConfigurationNode;
+import org.apache.commons.configuration.tree.DefaultConfigurationNode;
+import org.apache.commons.configuration.tree.ExpressionEngine;
+import org.apache.commons.configuration.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,34 +143,57 @@
  * 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 HierarchicalConfiguration} for querying or
+ * manipulating the hierarchy of configuration nodes, for instance the
+ * <code>configurationAt()</code> 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()</code> method. For obtaining a
+ * <code>SubnodeConfiguration</code> with the content of a specific section the
+ * <code>getSection()</code> 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
+ * @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
-     * <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();
@@ -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:
      *
-     * <pre>"value" ; comment -> value</pre>
+     * <pre>
+     * &quot;value&quot; ; comment -&gt; value
+     * </pre>
      *
-     * <pre>'value' ; comment -> value</pre>
+     * <pre>
+     * 'value' ; comment -&gt; value
+     * </pre>
      *
      * @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
+     * <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 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 <a
+ *         href="http://commons.apache.org/configuration/team-list.html">Commons
+ *         Configuration team</a>
  * @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.
       </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="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.



Mime
View raw message