You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by oh...@apache.org on 2009/03/17 22:40:10 UTC

svn commit: r755407 - in /commons/proper/configuration/trunk/src: java/org/apache/commons/configuration/ test/org/apache/commons/configuration/

Author: oheger
Date: Tue Mar 17 21:40:10 2009
New Revision: 755407

URL: http://svn.apache.org/viewvc?rev=755407&view=rev
Log:
CONFIGURATION-370: Introduced new IOFactory interface that allows injecting custom properties readers and writers.

Modified:
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfiguration.java
    commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfigurationLayout.java
    commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfiguration.java?rev=755407&r1=755406&r2=755407&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfiguration.java Tue Mar 17 21:40:10 2009
@@ -178,6 +178,12 @@
     static final String COMMENT_CHARS = "#!";
 
     /**
+     * Constant for the default <code>IOFactory</code>. This instance is used
+     * when no specific factory was set.
+     */
+    private static final IOFactory DEFAULT_IO_FACTORY = new DefaultIOFactory();
+
+    /**
      * This is the name of the property that can point to other
      * properties file for including other properties files.
      */
@@ -210,6 +216,9 @@
     /** Stores the layout object.*/
     private PropertiesConfigurationLayout layout;
 
+    /** The IOFactory for creating readers and writers.*/
+    private volatile IOFactory ioFactory;
+
     /** Allow file inclusion or not */
     private boolean includesAllowed;
 
@@ -402,6 +411,43 @@
     }
 
     /**
+     * Returns the <code>IOFactory</code> to be used for creating readers and
+     * writers when loading or saving this configuration.
+     *
+     * @return the <code>IOFactory</code>
+     * @since 1.7
+     */
+    public IOFactory getIOFactory()
+    {
+        return (ioFactory != null) ? ioFactory : DEFAULT_IO_FACTORY;
+    }
+
+    /**
+     * Sets the <code>IOFactory</code> to be used for creating readers and
+     * writers when loading or saving this configuration. Using this method a
+     * client can customize the reader and writer classes used by the load and
+     * save operations. Note that this method must be called before invoking
+     * one of the <code>load()</code> and <code>save()</code> methods.
+     * Especially, if you want to use a custom <code>IOFactory</code> for
+     * changing the <code>PropertiesReader</code>, you cannot load the
+     * configuration data in the constructor.
+     *
+     * @param ioFactory the new <code>IOFactory</code> (must not be <b>null</b>)
+     * @throws IllegalArgumentException if the <code>IOFactory</code> is
+     *         <b>null</b>
+     * @since 1.7
+     */
+    public void setIOFactory(IOFactory ioFactory)
+    {
+        if (ioFactory == null)
+        {
+            throw new IllegalArgumentException("IOFactory must not be null!");
+        }
+
+        this.ioFactory = ioFactory;
+    }
+
+    /**
      * Load the properties from the given reader.
      * Note that the <code>clear()</code> method is not called, so
      * the properties contained in the loaded file will be added to the
@@ -571,7 +617,7 @@
 
         /**
          * Creates a new instance of <code>PropertiesReader</code> and sets
-         * the underlaying reader and the list delimiter.
+         * the underlying reader and the list delimiter.
          *
          * @param reader the reader
          * @param listDelimiter the list delimiter character
@@ -1023,6 +1069,82 @@
     } // class PropertiesWriter
 
     /**
+     * <p>
+     * Definition of an interface that allows customization of read and write
+     * operations.
+     * </p>
+     * <p>
+     * For reading and writing properties files the inner classes
+     * <code>PropertiesReader</code> and <code>PropertiesWriter</code> are used.
+     * This interface defines factory methods for creating both a
+     * <code>PropertiesReader</code> and a <code>PropertiesWriter</code>. An
+     * object implementing this interface can be passed to the
+     * <code>setIOFactory()</code> method of
+     * <code>PropertiesConfiguration</code>. Every time the configuration is
+     * read or written the <code>IOFactory</code> is asked to create the
+     * appropriate reader or writer object. This provides an opportunity to
+     * inject custom reader or writer implementations.
+     * </p>
+     *
+     * @since 1.7
+     */
+    public static interface IOFactory
+    {
+        /**
+         * Creates a <code>PropertiesReader</code> for reading a properties
+         * file. This method is called whenever the
+         * <code>PropertiesConfiguration</code> is loaded. The reader returned
+         * by this method is then used for parsing the properties file.
+         *
+         * @param in the underlying reader (of the properties file)
+         * @param delimiter the delimiter character for list parsing
+         * @return the <code>PropertiesReader</code> for loading the
+         *         configuration
+         */
+        PropertiesReader createPropertiesReader(Reader in, char delimiter);
+
+        /**
+         * Creates a <code>PropertiesWriter</code> for writing a properties
+         * file. This method is called before the
+         * <code>PropertiesConfiguration</code> is saved. The writer returned by
+         * this method is then used for writing the properties file.
+         *
+         * @param out the underlying writer (to the properties file)
+         * @param delimiter the delimiter character for list parsing
+         * @return the <code>PropertiesWriter</code> for saving the
+         *         configuration
+         */
+        PropertiesWriter createPropertiesWriter(Writer out, char delimiter);
+    }
+
+    /**
+     * <p>
+     * A default implementation of the <code>IOFactory</code> interface.
+     * </p>
+     * <p>
+     * This class implements the <code>createXXXX()</code> methods defined by
+     * the <code>IOFactory</code> interface in a way that the default objects
+     * (i.e. <code>PropertiesReader</code> and <code>PropertiesWriter</code> are
+     * returned. Customizing either the reader or the writer (or both) can be
+     * done by extending this class and overriding the corresponding
+     * <code>createXXXX()</code> method.
+     * </p>
+     */
+    public static class DefaultIOFactory implements IOFactory
+    {
+        public PropertiesReader createPropertiesReader(Reader in, char delimiter)
+        {
+            return new PropertiesReader(in, delimiter);
+        }
+
+        public PropertiesWriter createPropertiesWriter(Writer out,
+                char delimiter)
+        {
+            return new PropertiesWriter(out, delimiter);
+        }
+    }
+
+    /**
      * <p>Unescapes any Java literals found in the <code>String</code> to a
      * <code>Writer</code>.</p> This is a slightly modified version of the
      * StringEscapeUtils.unescapeJava() function in commons-lang that doesn't

Modified: commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfigurationLayout.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfigurationLayout.java?rev=755407&r1=755406&r2=755407&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfigurationLayout.java (original)
+++ commons/proper/configuration/trunk/src/java/org/apache/commons/configuration/PropertiesConfigurationLayout.java Tue Mar 17 21:40:10 2009
@@ -381,8 +381,9 @@
         {
             getConfiguration().removeConfigurationListener(this);
         }
-        PropertiesConfiguration.PropertiesReader reader = new PropertiesConfiguration.PropertiesReader(
-                in, getConfiguration().getListDelimiter());
+        PropertiesConfiguration.PropertiesReader reader = getConfiguration()
+                .getIOFactory().createPropertiesReader(in,
+                        getConfiguration().getListDelimiter());
 
         try
         {
@@ -445,8 +446,8 @@
         {
             char delimiter = getConfiguration().isDelimiterParsingDisabled() ? 0
                     : getConfiguration().getListDelimiter();
-            PropertiesConfiguration.PropertiesWriter writer = new PropertiesConfiguration.PropertiesWriter(
-                    out, delimiter);
+            PropertiesConfiguration.PropertiesWriter writer = getConfiguration()
+                    .getIOFactory().createPropertiesWriter(out, delimiter);
             if (headerComment != null)
             {
                 writer.writeln(getCanonicalHeaderComment(true));

Modified: commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java?rev=755407&r1=755406&r2=755407&view=diff
==============================================================================
--- commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java (original)
+++ commons/proper/configuration/trunk/src/test/org/apache/commons/configuration/TestPropertiesConfiguration.java Tue Mar 17 21:40:10 2009
@@ -27,6 +27,7 @@
 import java.io.Reader;
 import java.io.StringReader;
 import java.io.StringWriter;
+import java.io.Writer;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLConnection;
@@ -47,14 +48,21 @@
  */
 public class TestPropertiesConfiguration extends TestCase
 {
+    /** Constant for a test property name.*/
+    private static final String PROP_NAME = "testProperty";
+
+    /** Constant for a test property value.*/
+    private static final String PROP_VALUE = "value";
+
+    /** The configuration to be tested.*/
     private PropertiesConfiguration conf;
 
     /** The File that we test with */
-    private String testProperties = new File("conf/test.properties").getAbsolutePath();
+    private static String testProperties = new File("conf/test.properties").getAbsolutePath();
 
-    private String testBasePath = new File("conf").getAbsolutePath();
-    private String testBasePath2 = new File("conf").getAbsoluteFile().getParentFile().getAbsolutePath();
-    private File testSavePropertiesFile = new File("target/testsave.properties");
+    private static String testBasePath = new File("conf").getAbsolutePath();
+    private static String testBasePath2 = new File("conf").getAbsoluteFile().getParentFile().getAbsolutePath();
+    private static File testSavePropertiesFile = new File("target/testsave.properties");
 
     protected void setUp() throws Exception
     {
@@ -428,12 +436,7 @@
             throws ConfigurationException
     {
         PropertiesConfiguration checkConfig = new PropertiesConfiguration(testSavePropertiesFile);
-        for (Iterator i = conf.getKeys(); i.hasNext();)
-        {
-            String key = (String) i.next();
-            assertTrue("The saved configuration doesn't contain the key '" + key + "'", checkConfig.containsKey(key));
-            assertEquals("Value of the '" + key + "' property", conf.getProperty(key), checkConfig.getProperty(key));
-        }
+        ConfigurationAssert.assertEquals(conf, checkConfig);
         return checkConfig;
     }
 
@@ -857,6 +860,91 @@
     }
 
     /**
+     * Tests whether a default IOFactory is set.
+     */
+    public void testGetIOFactoryDefault()
+    {
+        assertNotNull("No default IO factory", conf.getIOFactory());
+    }
+
+    /**
+     * Tests setting the IOFactory to null. This should cause an exception.
+     */
+    public void testSetIOFactoryNull()
+    {
+        try
+        {
+            conf.setIOFactory(null);
+            fail("Could set IO factory to null!");
+        }
+        catch (IllegalArgumentException iex)
+        {
+            // ok
+        }
+    }
+
+    /**
+     * Tests setting an IOFactory that uses a specialized reader.
+     */
+    public void testSetIOFactoryReader() throws ConfigurationException
+    {
+        final int propertyCount = 10;
+        conf.clear();
+        conf.setIOFactory(new PropertiesConfiguration.IOFactory()
+        {
+            public PropertiesConfiguration.PropertiesReader createPropertiesReader(
+                    Reader in, char delimiter)
+            {
+                return new PropertiesReaderTestImpl(in, delimiter,
+                        propertyCount);
+            }
+
+            public PropertiesConfiguration.PropertiesWriter createPropertiesWriter(
+                    Writer out, char delimiter)
+            {
+                throw new UnsupportedOperationException("Unexpected call!");
+            }
+        });
+        conf.load();
+        for (int i = 1; i <= propertyCount; i++)
+        {
+            assertEquals("Wrong property value at " + i, PROP_VALUE + i, conf
+                    .getString(PROP_NAME + i));
+        }
+    }
+
+    /**
+     * Tests setting an IOFactory that uses a specialized writer.
+     */
+    public void testSetIOFactoryWriter() throws ConfigurationException
+    {
+        conf.setIOFactory(new PropertiesConfiguration.IOFactory()
+        {
+            public PropertiesConfiguration.PropertiesReader createPropertiesReader(
+                    Reader in, char delimiter)
+            {
+                throw new UnsupportedOperationException("Unexpected call!");
+            }
+
+            public PropertiesConfiguration.PropertiesWriter createPropertiesWriter(
+                    Writer out, char delimiter)
+            {
+                try
+                {
+                    return new PropertiesWriterTestImpl(out, delimiter);
+                }
+                catch (IOException ioex)
+                {
+                    fail("Unexpected exception: " + ioex);
+                    return null;
+                }
+            }
+        });
+        conf.save(new StringWriter());
+        checkSavedConfig();
+    }
+
+    /**
      * Creates a configuration that can be used for testing copy operations.
      *
      * @return the configuration to be copied
@@ -987,4 +1075,58 @@
             return connection;
         }
     }
+
+    /**
+     * A test PropertiesReader for testing whether a custom reader can be
+     * injected. This implementation creates a configurable number of synthetic
+     * test properties.
+     */
+    private static class PropertiesReaderTestImpl extends
+            PropertiesConfiguration.PropertiesReader
+    {
+        /** The number of test properties to be created. */
+        private final int maxProperties;
+
+        /** The current number of properties. */
+        private int propertyCount;
+
+        public PropertiesReaderTestImpl(Reader reader, char listDelimiter,
+                int maxProps)
+        {
+            super(reader, listDelimiter);
+            assertEquals("Wrong list delimiter", ',', listDelimiter);
+            maxProperties = maxProps;
+        }
+
+        public String getPropertyName()
+        {
+            return PROP_NAME + propertyCount;
+        }
+
+        public String getPropertyValue()
+        {
+            return PROP_VALUE + propertyCount;
+        }
+
+        public boolean nextProperty() throws IOException
+        {
+            propertyCount++;
+            return propertyCount <= maxProperties;
+        }
+    }
+
+    /**
+     * A test PropertiesWriter for testing whether a custom writer can be
+     * injected. This implementation simply redirects all output into a test
+     * file.
+     */
+    private static class PropertiesWriterTestImpl extends
+            PropertiesConfiguration.PropertiesWriter
+    {
+        public PropertiesWriterTestImpl(Writer writer, char delimiter)
+                throws IOException
+        {
+            super(new FileWriter(testSavePropertiesFile), delimiter);
+        }
+    }
 }