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 2008/04/17 08:40:59 UTC
svn commit: r648976 - in
/commons/proper/configuration/branches/configuration2_experimental/src:
main/java/org/apache/commons/configuration2/PreferencesConfiguration.java
test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java
Author: oheger
Date: Wed Apr 16 23:40:56 2008
New Revision: 648976
URL: http://svn.apache.org/viewvc?rev=648976&view=rev
Log:
Initial implementation of PreferencesConfiguration
Added:
commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java (with props)
commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java (with props)
Added: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java?rev=648976&view=auto
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java (added)
+++ commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java Wed Apr 16 23:40:56 2008
@@ -0,0 +1,425 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration2;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+import org.apache.commons.configuration2.expr.AbstractNodeHandler;
+import org.apache.commons.configuration2.expr.ExpressionEngine;
+import org.apache.commons.configuration2.expr.def.DefaultExpressionEngine;
+
+/**
+ * <p>
+ * A configuration implementation on top of the Java <code>Preferences</code>
+ * API.
+ * </p>
+ * <p>
+ * This implementation of the <code>Configuration</code> interface is backed
+ * by a <code>java.util.prefs.Preferences</code> node. Query or update
+ * operations are directly performed on this node and its descendants. When
+ * creating this configuration the underlying <code>Preferences</code> node
+ * can be determined:
+ * <ul>
+ * <li>the system root node</li>
+ * <li>the user root node</li>
+ * <li>a system node corresponding to a specific package</li>
+ * <li>a user node corresponding to a specific package</li>
+ * </ul>
+ * This corresponds to the static factory methods provided by the
+ * <code>Preferences</code> class. It is also possible to change this node
+ * later by calling <code>setAssociatedClass()</code> or
+ * <code>setSystem()</code>.
+ * </p>
+ * <p>
+ * With this class the power provided by the <code>Configuration</code>
+ * interface can be used for interacting with <code>Preferences</code> nodes.
+ * Note however that some features provided by the <code>Configuration</code>
+ * interface are not supported by the <code>Preferences</code> API. One
+ * example of such a feature is the support for multiple values for a property.
+ * </p>
+ *
+ * @author Oliver Heger
+ * @version $Id$
+ */
+public class PreferencesConfiguration extends
+ AbstractHierarchicalConfiguration<Preferences>
+{
+ /** A lock for protecting the root node. */
+ private Lock lockRoot;
+
+ /** Stores the associated preferences node. */
+ private Preferences root;
+
+ /** Stores the class to be used when obtaining the root node. */
+ private Class<?> associatedClass;
+
+ /** Stores the system flag. */
+ private boolean system;
+
+ /**
+ * Creates a new instance of <code>PreferencesConfiguration</code> that
+ * accesses the user root node.
+ */
+ public PreferencesConfiguration()
+ {
+ this(false, null);
+ }
+
+ /**
+ * Creates a new instance of <code>PreferencesConfiguration</code> that
+ * accesses either the system or the user root node.
+ *
+ * @param system <b>true</b> for the system root node, <b>false</b> for
+ * the user root node
+ */
+ public PreferencesConfiguration(boolean system)
+ {
+ this(system, null);
+ }
+
+ /**
+ * Creates a new instance of <code>PreferencesConfiguration</code> that
+ * accesses the user preferences node associated with the package of the
+ * specified class.
+ *
+ * @param c the class defining the desired package
+ */
+ public PreferencesConfiguration(Class<?> c)
+ {
+ this(false, c);
+ }
+
+ /**
+ * Creates a new instance of <code>PreferencesConfiguration</code> that
+ * accesses the node associated with the package of the specified class in
+ * either the user or the system space.
+ *
+ * @param system <b>true</b> for the system root node, <b>false</b> for
+ * the user root node
+ * @param c the class defining the desired package
+ */
+ public PreferencesConfiguration(boolean system, Class<?> c)
+ {
+ super(new PreferencesNodeHandler());
+ lockRoot = new ReentrantLock();
+ setExpressionEngine(setUpExpressionEngine());
+ setAssociatedClass(c);
+ setSystem(system);
+ }
+
+ /**
+ * Returns the class associated with this configuration.
+ *
+ * @return the associated class
+ */
+ public Class<?> getAssociatedClass()
+ {
+ return associatedClass;
+ }
+
+ /**
+ * Sets the associated class. When obtaining the associated
+ * <code>Preferences</code> node, this class is passed in.
+ *
+ * @param associatedClass the associated class
+ */
+ public void setAssociatedClass(Class<?> associatedClass)
+ {
+ this.associatedClass = associatedClass;
+ clearRootNode();
+ }
+
+ /**
+ * Returns the system flag. This flag determines whether system or user
+ * preferences are used.
+ *
+ * @return the system flag
+ */
+ public boolean isSystem()
+ {
+ return system;
+ }
+
+ /**
+ * Sets the system flag. This flag determines whether system or user
+ * preferences are used.
+ *
+ * @param system the system flag
+ */
+ public void setSystem(boolean system)
+ {
+ this.system = system;
+ clearRootNode();
+ }
+
+ /**
+ * Returns the root node of this configuration. This is a
+ * <code>Preferences</code> node, which is specified by the properties
+ * <code>associatedClass</code> and <code>system</code>.
+ *
+ * @return the root node
+ */
+ @Override
+ public Preferences getRootNode()
+ {
+ lockRoot.lock();
+ try
+ {
+ if (root == null)
+ {
+ root = createRootNode();
+ }
+ return root;
+ }
+ finally
+ {
+ lockRoot.unlock();
+ }
+ }
+
+ /**
+ * Saves all changes made at this configuration. Calls <code>flush()</code>
+ * on the underlying <code>Preferences</code> node. This causes the
+ * preferences to be written back to the backing store.
+ *
+ * @throws ConfigurationRuntimeException if an error occurs
+ */
+ public void flush()
+ {
+ try
+ {
+ getRootNode().flush();
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(
+ "Could not flush configuration", bex);
+ }
+ }
+
+ /**
+ * Creates the root node of this configuration. This method has to evaluate
+ * the system flag and the package to obtain the correct preferences node.
+ *
+ * @return the root preferences node of this configuration
+ */
+ protected Preferences createRootNode()
+ {
+ if (getAssociatedClass() != null)
+ {
+ return isSystem() ? Preferences
+ .systemNodeForPackage(getAssociatedClass()) : Preferences
+ .userNodeForPackage(getAssociatedClass());
+ }
+ else
+ {
+ return isSystem() ? Preferences.systemRoot() : Preferences
+ .userRoot();
+ }
+ }
+
+ /**
+ * Creates the expression engine to be used by this configuration. This
+ * implementation returns an expression engine that uses dots for both nodes
+ * and attributes.
+ *
+ * @return the expression engine to use
+ */
+ protected ExpressionEngine setUpExpressionEngine()
+ {
+ DefaultExpressionEngine ex = new DefaultExpressionEngine();
+ ex.setAttributeEnd(null);
+ ex.setAttributeStart(ex.getPropertyDelimiter());
+ return ex;
+ }
+
+ /**
+ * Resets the root node. This method is called when the parameters of this
+ * configuration were been changed. When the root node is accessed next
+ * time, it is re-created.
+ */
+ private void clearRootNode()
+ {
+ lockRoot.lock();
+ try
+ {
+ root = null;
+ }
+ finally
+ {
+ lockRoot.unlock();
+ }
+ }
+
+ /**
+ * The node handler for dealing with <code>Preferences</code> nodes.
+ */
+ private static class PreferencesNodeHandler extends
+ AbstractNodeHandler<Preferences>
+ {
+
+ public void addAttributeValue(Preferences node, String name,
+ Object value)
+ {
+ node.put(name, String.valueOf(value));
+ }
+
+ public Preferences addChild(Preferences node, String name)
+ {
+ return node.node(name);
+ }
+
+ @Override
+ public Preferences addChild(Preferences node, String name, Object value)
+ {
+ addAttributeValue(node, name, value);
+ return null;
+ }
+
+ public Object getAttributeValue(Preferences node, String name)
+ {
+ return node.get(name, null);
+ }
+
+ public List<String> getAttributes(Preferences node)
+ {
+ try
+ {
+ return Arrays.asList(node.keys());
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(bex);
+ }
+ }
+
+ public Preferences getChild(Preferences node, int index)
+ {
+ try
+ {
+ String[] childrenNames = node.childrenNames();
+ return node.node(childrenNames[index]);
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(bex);
+ }
+ }
+
+ public List<Preferences> getChildren(Preferences node)
+ {
+ try
+ {
+ String[] childrenNames = node.childrenNames();
+ List<Preferences> result = new ArrayList<Preferences>(
+ childrenNames.length);
+ for (String name : childrenNames)
+ {
+ result.add(node.node(name));
+ }
+ return result;
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(bex);
+ }
+ }
+
+ public List<Preferences> getChildren(Preferences node, String name)
+ {
+ try
+ {
+ if (node.nodeExists(name))
+ {
+ return Collections.singletonList(node.node(name));
+ }
+ else
+ {
+ return Collections.emptyList();
+ }
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(bex);
+ }
+ }
+
+ public int getChildrenCount(Preferences node, String name)
+ {
+ try
+ {
+ String[] childrenNames = node.childrenNames();
+ return childrenNames.length;
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(bex);
+ }
+ }
+
+ public Preferences getParent(Preferences node)
+ {
+ return node.parent();
+ }
+
+ public Object getValue(Preferences node)
+ {
+ return null;
+ }
+
+ public String nodeName(Preferences node)
+ {
+ return node.name();
+ }
+
+ public void removeAttribute(Preferences node, String name)
+ {
+ node.remove(name);
+ }
+
+ public void removeChild(Preferences node, Preferences child)
+ {
+ try
+ {
+ child.removeNode();
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(bex);
+ }
+ }
+
+ public void setAttributeValue(Preferences node, String name,
+ Object value)
+ {
+ addAttributeValue(node, name, value);
+ }
+
+ public void setValue(Preferences node, Object value)
+ {
+ throw new UnsupportedOperationException(
+ "Cannot set a value of a Preferences node!");
+ }
+ }
+}
Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: commons/proper/configuration/branches/configuration2_experimental/src/main/java/org/apache/commons/configuration2/PreferencesConfiguration.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java
URL: http://svn.apache.org/viewvc/commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java?rev=648976&view=auto
==============================================================================
--- commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java (added)
+++ commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java Wed Apr 16 23:40:56 2008
@@ -0,0 +1,314 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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 License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.commons.configuration2;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+import junit.framework.TestCase;
+
+/**
+ * Test class for PreferencesConfiguration.
+ *
+ * @author Oliver Heger
+ * @version $Id$
+ */
+public class TestPreferencesConfiguration extends TestCase
+{
+ /** Constant for the node with the test data. */
+ private static final String TEST_NODE = "PreferencesConfigurationTest";
+
+ /** A preferences node with the test data. */
+ private Preferences node;
+
+ /**
+ * Clears the test environment. If the test node was created, it is now
+ * removed.
+ */
+ @Override
+ protected void tearDown() throws Exception
+ {
+ if (node != null && node.nodeExists(TEST_NODE))
+ {
+ Preferences testNode = node.node(TEST_NODE);
+ testNode.removeNode();
+ }
+
+ super.tearDown();
+ }
+
+ /**
+ * Helper method for creating the final key for the given key. Adds the name
+ * of the test node.
+ *
+ * @param k the key
+ * @return the final key
+ */
+ private static String key(String k)
+ {
+ StringBuilder buf = new StringBuilder(TEST_NODE);
+ buf.append('.').append(k);
+ return buf.toString();
+ }
+
+ /**
+ * Adds some test data to the test preferences node.
+ */
+ private void setUpTestData()
+ {
+ Preferences testNode = node.node(TEST_NODE);
+ testNode.putBoolean("test", true);
+ Preferences guiNode = testNode.node("gui");
+ guiNode.put("background", "black");
+ guiNode.put("foreground", "blue");
+ Preferences dbNode = testNode.node("db");
+ dbNode.put("user", "scott");
+ dbNode.put("pwd", "tiger");
+ Preferences tabNode = dbNode.node("tables");
+ tabNode.put("tab1", "users");
+ tabNode.put("tab2", "documents");
+ try
+ {
+ testNode.flush();
+ }
+ catch (BackingStoreException bex)
+ {
+ throw new ConfigurationRuntimeException(bex);
+ }
+ }
+
+ /**
+ * Tests whether the configuration contains the expected properties.
+ *
+ * @param config the test configuration
+ */
+ private void checkProperties(PreferencesConfiguration config)
+ {
+ assertTrue("Wrong value for test", config.getBoolean(key("test")));
+ assertEquals("Wrong value for background", "black", config
+ .getString(key("gui.background")));
+ assertEquals("Wrong value for foreground", "blue", config
+ .getString(key("gui.foreground")));
+ assertEquals("Wrong value for user", "scott", config
+ .getString(key("db.user")));
+ assertEquals("Wrong value for pwd", "tiger", config
+ .getString(key("db.pwd")));
+ assertEquals("Wrong value for tab1", "users", config
+ .getString(key("db.tables.tab1")));
+ assertEquals("Wrong value for tab2", "documents", config
+ .getString(key("db.tables.tab2")));
+ }
+
+ /**
+ * Creates some test data and a configuration for accessing it.
+ *
+ * @return the test configuration
+ */
+ private PreferencesConfiguration setUpTestConfig()
+ {
+ node = Preferences.systemNodeForPackage(getClass());
+ setUpTestData();
+ return new PreferencesConfiguration(true, getClass());
+ }
+
+ /**
+ * Tests querying properties from the system root node.
+ */
+ public void testGetPropertiesSystem()
+ {
+ node = Preferences.systemRoot();
+ setUpTestData();
+ PreferencesConfiguration config = new PreferencesConfiguration(true);
+ checkProperties(config);
+ }
+
+ /**
+ * Tests querying properties from the user root node.
+ */
+ public void testGetPropertiesUser()
+ {
+ node = Preferences.userRoot();
+ setUpTestData();
+ PreferencesConfiguration config = new PreferencesConfiguration();
+ checkProperties(config);
+ }
+
+ /**
+ * Tests querying properties from the system node with the given package.
+ */
+ public void testGetPropertiesSystemPackage()
+ {
+ node = Preferences.systemNodeForPackage(getClass());
+ setUpTestData();
+ PreferencesConfiguration config = new PreferencesConfiguration(true,
+ getClass());
+ checkProperties(config);
+ }
+
+ /**
+ * Tests querying properties from the user node with the given package.
+ */
+ public void testGetPropertiesUserPackage()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ checkProperties(config);
+ }
+
+ /**
+ * Tests whether changing the configuration's parameters causes the root
+ * node to be re-created.
+ */
+ public void testChangeParameters()
+ {
+ PreferencesConfiguration config = new PreferencesConfiguration();
+ Preferences root = config.getRootNode();
+ config.setAssociatedClass(getClass());
+ assertNotSame("Root node not changed", root, config.getRootNode());
+ }
+
+ /**
+ * Tests whether the expected keys are returned.
+ */
+ public void testGetKeys()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ Set<String> keys = new HashSet<String>();
+ for (Iterator<String> it = config.getKeys(); it.hasNext();)
+ {
+ keys.add(it.next());
+ }
+ assertTrue("Key not found: background", keys
+ .contains(key("gui.background")));
+ assertTrue("Key not found: user", keys.contains(key("db.user")));
+ assertTrue("Key not found: tab1", keys.contains(key("db.tables.tab1")));
+ }
+
+ /**
+ * Tests the isEmpty() method.
+ */
+ public void testIsEmpty()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ assertFalse("Configuration is empty", config.isEmpty());
+ }
+
+ /**
+ * Tests adding a new property.
+ */
+ public void testAddProperty()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ config.addProperty(key("anotherTest"), Boolean.TRUE);
+ config.addProperty(key("db.url"), "testdb");
+ config.flush();
+ Preferences nd = node.node(TEST_NODE);
+ assertTrue("test key not set", nd.getBoolean("anotherTest", false));
+ nd = nd.node("db");
+ assertEquals("Db property not set", "testdb", nd.get("url", null));
+ }
+
+ /**
+ * Tests the addProperty() method when a new node has to be added.
+ */
+ public void testAddPropertyNewNode()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ config.addProperty(key("db.meta.session.mode"), "debug");
+ config.flush();
+ Preferences nd = node.node(TEST_NODE + "/db/meta/session");
+ assertEquals("Property not added", "debug", nd.get("mode", null));
+ }
+
+ /**
+ * Tests overriding a property.
+ */
+ public void testSetProperty()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ config.setProperty(key("db.user"), "harry");
+ config.setProperty(key("db.url"), "testdb");
+ Preferences nd = node.node(TEST_NODE + "/db");
+ assertEquals("Property not added", "testdb", nd.get("url", null));
+ assertEquals("Property not set", "harry", nd.get("user", null));
+ }
+
+ /**
+ * Tests whether a property can be removed.
+ */
+ public void testClearProperty() throws BackingStoreException
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ config.clearProperty(key("db.tables.tab1"));
+ Preferences nd = node.node(TEST_NODE + "/db/tables");
+ String[] keys = nd.keys();
+ assertEquals("Key not removed", 1, keys.length);
+ assertEquals("Wrong key removed", "tab2", keys[0]);
+ }
+
+ /**
+ * Tests removing a whole property tree.
+ */
+ public void testClearTree() throws BackingStoreException
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ config.clearTree(key("db"));
+ assertFalse("Node not removed", node.nodeExists(TEST_NODE + "/db"));
+ }
+
+ /**
+ * Tests querying the number of property values.
+ */
+ public void testGetMaxIndex()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ assertEquals("Wrong number of values", 0, config
+ .getMaxIndex(key("db.user")));
+ assertEquals("Wrong number of values for node", 0, config
+ .getMaxIndex(key("db")));
+ assertEquals("Wrong number of values for non ex. property", -1, config
+ .getMaxIndex("non.ex.key"));
+ }
+
+ /**
+ * Tests obtaining a sub configuration.
+ */
+ public void testConfigurationAt()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ SubConfiguration<Preferences> sub = config.configurationAt(TEST_NODE);
+ assertEquals("Wrong user", "scott", sub.getString("db.user"));
+ }
+
+ /**
+ * Tests modifying a sub configuration.
+ */
+ public void testConfigurationAtModify()
+ {
+ PreferencesConfiguration config = setUpTestConfig();
+ SubConfiguration<Preferences> sub = config.configurationAt(TEST_NODE);
+ sub.setProperty("db.user", "harry");
+ config.setProperty(key("db.pwd"), "dolphin");
+ sub.addProperty("db.url", "testdb");
+ assertEquals("User not changed", "harry", config
+ .getString(key("db.user")));
+ assertEquals("URL not found", "testdb", config.getString(key("db.url")));
+ assertEquals("Pwd not changed", "dolphin", sub.getString("db.pwd"));
+ }
+}
Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java
------------------------------------------------------------------------------
svn:keywords = Date Author Id Revision HeadURL
Propchange: commons/proper/configuration/branches/configuration2_experimental/src/test/java/org/apache/commons/configuration2/TestPreferencesConfiguration.java
------------------------------------------------------------------------------
svn:mime-type = text/plain