commons-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ohe...@apache.org
Subject 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
Date Tue, 18 Nov 2008 21:29:33 GMT
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>
+     * &quot;value&quot; ; comment -&gt; value
+     * </pre>
      *
-     * <pre>'value' ; comment -> value</pre>
+     * <pre>
+     * 'value' ; comment -&gt; 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>



Mime
View raw message