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);
+ }
+ }
}