summaryrefslogtreecommitdiffstats
path: root/prefs
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2008-10-21 07:00:00 -0700
commitfdb2704414a9ed92394ada0d1395e4db86889465 (patch)
tree9b591a4a50054274a197f02b3ccb51313681879f /prefs
downloadlibcore-fdb2704414a9ed92394ada0d1395e4db86889465.zip
libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.gz
libcore-fdb2704414a9ed92394ada0d1395e4db86889465.tar.bz2
Initial Contribution
Diffstat (limited to 'prefs')
-rw-r--r--prefs/MODULE_LICENSE_APACHE20
-rw-r--r--prefs/src/main/java/java/util/prefs/AbstractPreferences.java936
-rw-r--r--prefs/src/main/java/java/util/prefs/BackingStoreException.java53
-rw-r--r--prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java44
-rw-r--r--prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java236
-rw-r--r--prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java66
-rw-r--r--prefs/src/main/java/java/util/prefs/NodeChangeEvent.java97
-rw-r--r--prefs/src/main/java/java/util/prefs/NodeChangeListener.java49
-rw-r--r--prefs/src/main/java/java/util/prefs/NodeSet.java34
-rw-r--r--prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java112
-rw-r--r--prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java45
-rw-r--r--prefs/src/main/java/java/util/prefs/Preferences.java933
-rw-r--r--prefs/src/main/java/java/util/prefs/PreferencesFactory.java48
-rw-r--r--prefs/src/main/java/java/util/prefs/XMLParser.java596
-rw-r--r--prefs/src/main/java/java/util/prefs/package.html11
-rw-r--r--prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java132
-rw-r--r--prefs/src/main/java/org/apache/harmony/prefs/internal/nls/messages.properties33
-rw-r--r--prefs/src/main/resources/META-INF/services/java.util.prefs.PreferencesFactory1
-rw-r--r--prefs/src/test/java/java/util/prefs/FilePreferencesFactoryTestImpl.java44
-rw-r--r--prefs/src/test/java/java/util/prefs/FilePreferencesTestImpl.java236
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java1881
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AllTests.java48
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.java66
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java214
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java83
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java245
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockNodeChangeListener.java165
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferenceChangeListener.java102
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferencesFactory.java42
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java60
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java115
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java67
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java151
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java52
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java59
-rw-r--r--prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java1453
-rw-r--r--prefs/src/test/java/tests/prefs/AllTests.java38
-rw-r--r--prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd56
-rw-r--r--prefs/src/test/resources/prefs/java/util/prefs/userprefs-ascii.xml63
-rw-r--r--prefs/src/test/resources/prefs/java/util/prefs/userprefs-badencoding.xml64
-rw-r--r--prefs/src/test/resources/prefs/java/util/prefs/userprefs-badform.xml54
-rw-r--r--prefs/src/test/resources/prefs/java/util/prefs/userprefs-badtype.xml64
-rw-r--r--prefs/src/test/resources/prefs/java/util/prefs/userprefs-higherversion.xml64
-rw-r--r--prefs/src/test/resources/prefs/java/util/prefs/userprefs.xml76
-rw-r--r--prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.golden.serbin0 -> 1547 bytes
-rw-r--r--prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.golden.serbin0 -> 1583 bytes
46 files changed, 8988 insertions, 0 deletions
diff --git a/prefs/MODULE_LICENSE_APACHE2 b/prefs/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/prefs/MODULE_LICENSE_APACHE2
diff --git a/prefs/src/main/java/java/util/prefs/AbstractPreferences.java b/prefs/src/main/java/java/util/prefs/AbstractPreferences.java
new file mode 100644
index 0000000..59f9a65
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/AbstractPreferences.java
@@ -0,0 +1,936 @@
+/*
+ * 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 java.util.prefs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.util.EventListener;
+import java.util.EventObject;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeSet;
+
+import org.apache.harmony.luni.util.Base64;
+import org.apache.harmony.prefs.internal.nls.Messages;
+
+/**
+ * This class is partly implementation of <code>Preferences</code>, which can be
+ * used to simplify <code>Preferences</code> provider's implementation.
+ * <p>
+ * This class define nine abstract SPI methods, which must be implemented by
+ * preference provider. And provider can also override other methods of this
+ * class. Some SPI methods will throw <code>BackingStoreException</code>,
+ * including <code>childrenNamesSpi()</code>, <code>flushSpi()</code>,
+ * <code>keysSpi()</code>, <code>removeNodeSpi()</code>,
+ * <code>syncSpi()</code>; <code>getSpi(String, String)</code> never throws any
+ * exceptions; the last SPI methods, <code>putSpi(String)</code>,
+ * <code>removeSpi(String)</code> and <code>childSpi(String)</code> won't throw
+ * <code>BackingStoreException</code>, but in some implementations, they may
+ * throw <code>SecurityException</code> due to lacking the permission to access
+ * backing end storage.</p>
+ *
+ * @since 1.4
+ * @see Preferences
+ */
+public abstract class AbstractPreferences extends Preferences {
+ /*
+ * -----------------------------------------------------------
+ * Class fields
+ * -----------------------------------------------------------
+ */
+ /** the unhandled events collection */
+ private static final List<EventObject> events = new LinkedList<EventObject>();
+ /** the event dispatcher thread */
+ private static final EventDispatcher dispatcher = new EventDispatcher("Preference Event Dispatcher"); //$NON-NLS-1$
+
+ /*
+ * -----------------------------------------------------------
+ * Class initializer
+ * -----------------------------------------------------------
+ */
+ static {
+ dispatcher.setDaemon(true);
+ dispatcher.start();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ @Override
+ public void run() {
+ Preferences uroot = Preferences.userRoot();
+ Preferences sroot = Preferences.systemRoot();
+ try {
+ uroot.flush();
+ } catch (BackingStoreException e) {//ignore
+ }
+ try {
+ sroot.flush();
+ } catch (BackingStoreException e) {//ignore
+ }
+ }
+ });
+ }
+
+ /*
+ * -----------------------------------------------------------
+ * Instance fields (package-private)
+ * -----------------------------------------------------------
+ */
+ /** true if this node is in user preference hierarchy */
+ boolean userNode;
+
+ /*
+ * -----------------------------------------------------------
+ * Instance fields (private)
+ * -----------------------------------------------------------
+ */
+ /** Marker class for 'lock' field. */
+ private static class Lock {}
+ /** The object used to lock this node. */
+ protected final Object lock;
+
+ /**
+ * This field is true if this node is created while it doesn't exist in the
+ * backing store. This field's default value is false, and it is checked when
+ * the node creation is completed, and if it is true, the node change event
+ * will be fired for this node's parent.
+ */
+ protected boolean newNode;
+
+ /** cached child nodes */
+ private Map<String, AbstractPreferences> cachedNode;
+
+ //the collections of listeners
+ private List<EventListener> nodeChangeListeners;
+ private List<EventListener> preferenceChangeListeners;
+
+ //this node's name
+ private String nodeName;
+
+ //handler to this node's parent
+ private AbstractPreferences parentPref;
+
+ //true if this node has been removed
+ private boolean isRemoved;
+
+ //handler to this node's root node
+ private AbstractPreferences root;
+
+ /*
+ * -----------------------------------------------------------
+ * Constructors
+ * -----------------------------------------------------------
+ */
+ /**
+ * Construct a new <code>AbstractPreferences</code> instance using given
+ * parent node and node name.
+ *
+ * @param parent
+ * the parent node of this node, can be null, which means this
+ * node is root
+ * @param name
+ * the name of this node, can be empty(""), which means this
+ * node is root
+ * @throws IllegalArgumentException
+ * if name contains slash, or be empty if parent is not null
+ */
+ protected AbstractPreferences(AbstractPreferences parent, String name) {
+ if ((null == parent ^ name.length() == 0) || name.indexOf("/") >= 0) { //$NON-NLS-1$
+ throw new IllegalArgumentException();
+ }
+ root = null == parent ? this : parent.root;
+ nodeChangeListeners = new LinkedList<EventListener>();
+ preferenceChangeListeners = new LinkedList<EventListener>();
+ isRemoved = false;
+ cachedNode = new HashMap<String, AbstractPreferences>();
+ nodeName = name;
+ parentPref = parent;
+ lock = new Lock();
+ userNode = root.userNode;
+ }
+
+ /*
+ * -----------------------------------------------------------
+ * Methods
+ * -----------------------------------------------------------
+ */
+ /**
+ * Return arrays of all cached children node.
+ *
+ * @return arrays of all cached children node.
+ */
+ protected final AbstractPreferences[] cachedChildren() {
+ return cachedNode.values().toArray(new AbstractPreferences[cachedNode.size()]);
+ }
+
+ /**
+ * Return the child node with given name, or null if it doesn't exist. The
+ * given name must be valid and this node cannot be removed. Invocation of
+ * this method implies that the node with given name is not cached(or, has
+ * been removed.)
+ *
+ * @param name the given child name to be got
+ * @return the child node with given name, or null if it doesn't exist
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ protected AbstractPreferences getChild(String name)
+ throws BackingStoreException {
+ synchronized (lock) {
+ checkState();
+ AbstractPreferences result = null;
+ String[] childrenNames = childrenNames();
+ for (int i = 0; i < childrenNames.length; i++) {
+ if (childrenNames[i].equals(name)) {
+ result = childSpi(name);
+ break;
+ }
+ }
+ return result;
+ }
+
+ }
+
+ /**
+ * Return true if and only if this node has been removed by invoking
+ * {@link #removeNode() removeNode}.
+ *
+ * @return true if and only if this node has been removed by invoking
+ * {@link #removeNode() removeNode}
+ */
+ protected boolean isRemoved() {
+ synchronized (lock) {
+ return isRemoved;
+ }
+ }
+
+ /**
+ * Flush changes of this node to the backing store. This method should only
+ * flush this node, and should not include the descendant nodes. The
+ * implementation which want to flush all nodes at once should override
+ * {@link #flush() flush()} method.
+ *
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ protected abstract void flushSpi() throws BackingStoreException;
+
+ /**
+ * Return names of this node's all children , or empty array if this node has
+ * no child. Cached children name is not required to be returned.
+ *
+ * @return names of this node's all children
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ protected abstract String[] childrenNamesSpi() throws BackingStoreException;
+
+ /**
+ * Return the child preference node with the given name, and create new one if
+ * it does not exist. Invoker of this method should assure that the given name
+ * are valid as well as this node is not removed. Invocation of this method
+ * implies that the node with given name is not cached(or, has been removed.)
+ * If the named node has just been removed, implementation of this method must
+ * create a new one instead of reactivated the removed one.
+ * <p>
+ * The new creation is not required to be persisted immediately until the flush
+ * method is invoked.</p>
+ *
+ * @param name
+ * @return AbstractPreferences
+ */
+ protected abstract AbstractPreferences childSpi(String name);
+
+
+ /**
+ * Put the given key-value pair into this node. Invoker of this method should
+ * assure that both the given values are valid as well as this node
+ * is not removed.
+ *
+ * @param name the given preference key
+ * @param value the given preference value
+ */
+ protected abstract void putSpi(String name, String value);
+
+ /**
+ * Get the preference value mapped to the given key. Invoker of this method
+ * should assure that given key are valid as well as this node is not removed.
+ * This method should not throw exceptions, but if it does, the invoker should
+ * catch it and deal with it as null return value.
+ *
+ * @param key the given key to be searched for
+ * @return the preference value mapped to the given key
+ */
+ protected abstract String getSpi(String key);
+
+
+ /**
+ * Return all keys of this node's preferences, or empty array if no preference
+ * found on this node. Invoker of this method should assure that this node is
+ * not removed.
+ *
+ * @return all keys of this node's preferences
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ protected abstract String[] keysSpi() throws BackingStoreException;
+
+ /**
+ * Remove this node from the preference hierarchy tree. The invoker of this
+ * method should assure that this node has no child node, which means the
+ * {@link Preferences#removeNode() Preferences.removeNode()} should invoke
+ * this method multi-times in bottom-up pattern. The removal is not required
+ * to be persisted at once until the it is flushed.
+ *
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ protected abstract void removeNodeSpi() throws BackingStoreException;
+
+ /**
+ * Remove the preference with the given key. Invoker of this method
+ * should assure that given key are valid as well as this node is not removed.
+ *
+ * @param key the given key to removed
+ */
+ protected abstract void removeSpi(String key);
+
+ /**
+ * Synchronize this node with the backing store. This method should only
+ * synchronize this node, and should not include the descendant nodes. The
+ * implementation which want to synchronize all nodes at once should override
+ * {@link #sync() sync()} method.
+ *
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ protected abstract void syncSpi() throws BackingStoreException;
+
+ /*
+ * -----------------------------------------------------------
+ * Methods inherited from Preferences
+ * -----------------------------------------------------------
+ */
+ @Override
+ public String absolutePath() {
+ if (parentPref == null) {
+ return "/"; //$NON-NLS-1$
+ } else if (parentPref == root) {
+ return "/" + nodeName; //$NON-NLS-1$
+ }
+ return parentPref.absolutePath() + "/" + nodeName; //$NON-NLS-1$
+ }
+
+ @Override
+ public String[] childrenNames() throws BackingStoreException {
+ synchronized (lock) {
+ checkState();
+ TreeSet<String> result = new TreeSet<String>(cachedNode.keySet());
+ String[] names = childrenNamesSpi();
+ for (int i = 0; i < names.length; i++) {
+ result.add(names[i]);
+ }
+ return result.toArray(new String[0]);
+ }
+ }
+
+ @Override
+ public void clear() throws BackingStoreException {
+ synchronized (lock) {
+ String[] keyList = keys();
+ for (int i = 0; i < keyList.length; i++) {
+ remove(keyList[i]);
+ }
+ }
+ }
+
+ @Override
+ public void exportNode(OutputStream ostream) throws IOException,
+ BackingStoreException {
+ if(ostream == null) {
+ // prefs.5=Stream is null
+ throw new NullPointerException(Messages.getString("prefs.5")); //$NON-NLS-1$
+ }
+ checkState();
+ XMLParser.exportPrefs(this, ostream, false);
+
+ }
+
+ @Override
+ public void exportSubtree(OutputStream ostream) throws IOException,
+ BackingStoreException {
+ if(ostream == null) {
+ // prefs.5=Stream is null
+ throw new NullPointerException(Messages.getString("prefs.5")); //$NON-NLS-1$
+ }
+ checkState();
+ XMLParser.exportPrefs(this, ostream, true);
+ }
+
+ @Override
+ public void flush() throws BackingStoreException {
+ synchronized (lock) {
+ flushSpi();
+ }
+ AbstractPreferences[] cc = cachedChildren();
+ int i;
+ for (i = 0; i < cc.length; i++) {
+ cc[i].flush();
+ }
+ }
+
+ @Override
+ public String get(String key, String deflt) {
+ if (key == null) {
+ throw new NullPointerException();
+ }
+ String result;
+ synchronized (lock) {
+ checkState();
+ try {
+ result = getSpi(key);
+ } catch (Exception e) {
+ result = null;
+ }
+ }
+ return (result == null ? deflt : result);
+ }
+
+ @Override
+ public boolean getBoolean(String key, boolean deflt) {
+ String result = get(key, null);
+ if (result == null) {
+ return deflt;
+ } else if (result.equalsIgnoreCase("true")) { //$NON-NLS-1$
+ return true;
+ } else if (result.equalsIgnoreCase("false")) { //$NON-NLS-1$
+ return false;
+ } else {
+ return deflt;
+ }
+ }
+
+ @Override
+ public byte[] getByteArray(String key, byte[] deflt) {
+ String svalue = get(key, null);
+ if (svalue == null) {
+ return deflt;
+ }
+ if (svalue.length() == 0) {
+ return new byte[0];
+ }
+ byte[] dres;
+ try {
+ byte[] bavalue = svalue.getBytes("US-ASCII"); //$NON-NLS-1$
+ if (bavalue.length % 4 != 0) {
+ return deflt;
+ }
+ dres = Base64.decode(bavalue);
+ } catch (Exception e) {
+ dres = deflt;
+ }
+ return dres;
+ }
+
+ @Override
+ public double getDouble(String key, double deflt) {
+ String result = get(key, null);
+ if (result == null) {
+ return deflt;
+ }
+ double dres;
+ try {
+ dres = Double.parseDouble(result);
+ } catch (NumberFormatException e) {
+ dres = deflt;
+ }
+ return dres;
+ }
+
+ @Override
+ public float getFloat(String key, float deflt) {
+ String result = get(key, null);
+ if (result == null) {
+ return deflt;
+ }
+ float fres;
+ try {
+ fres = Float.parseFloat(result);
+ } catch (NumberFormatException e) {
+ fres = deflt;
+ }
+ return fres;
+ }
+
+ @Override
+ public int getInt(String key, int deflt) {
+ String result = get(key, null);
+ if (result == null) {
+ return deflt;
+ }
+ int ires;
+ try {
+ ires = Integer.parseInt(result);
+ } catch (NumberFormatException e) {
+ ires = deflt;
+ }
+ return ires;
+ }
+
+ @Override
+ public long getLong(String key, long deflt) {
+ String result = get(key, null);
+ if (result == null) {
+ return deflt;
+ }
+ long lres;
+ try {
+ lres = Long.parseLong(result);
+ } catch (NumberFormatException e) {
+ lres = deflt;
+ }
+ return lres;
+ }
+
+ @Override
+ public boolean isUserNode() {
+ return root == Preferences.userRoot();
+ }
+
+ @Override
+ public String[] keys() throws BackingStoreException {
+ synchronized (lock) {
+ checkState();
+ return keysSpi();
+ }
+ }
+
+ @Override
+ public String name() {
+ return nodeName;
+ }
+
+ @Override
+ public Preferences node(String name) {
+ AbstractPreferences startNode = null;
+ synchronized (lock) {
+ checkState();
+ validateName(name);
+ if ("".equals(name)) { //$NON-NLS-1$
+ return this;
+ } else if ("/".equals(name)) { //$NON-NLS-1$
+ return root;
+ }
+ if (name.startsWith("/")) { //$NON-NLS-1$
+ startNode = root;
+ name = name.substring(1);
+ } else {
+ startNode = this;
+ }
+ }
+ Preferences result = null;
+ try {
+ result = startNode.nodeImpl(name, true);
+ } catch (BackingStoreException e) {
+ //should not happen
+ }
+ return result;
+ }
+
+ private void validateName(String name) {
+ if (name.endsWith("/") && name.length() > 1) { //$NON-NLS-1$
+ // prefs.6=Name cannot end with '/'\!
+ throw new IllegalArgumentException(Messages.getString("prefs.6")); //$NON-NLS-1$
+ }
+ if (name.indexOf("//") >= 0) { //$NON-NLS-1$
+ // prefs.7=Name cannot contains consecutive '/'\!
+ throw new IllegalArgumentException(
+ Messages.getString("prefs.7")); //$NON-NLS-1$
+ }
+ }
+
+ private AbstractPreferences nodeImpl(String path, boolean createNew)
+ throws BackingStoreException {
+ StringTokenizer st = new StringTokenizer(path, "/"); //$NON-NLS-1$
+ AbstractPreferences currentNode = this;
+ AbstractPreferences temp = null;
+ while (st.hasMoreTokens() && null != currentNode) {
+ String name = st.nextToken();
+ synchronized (currentNode.lock) {
+ temp = currentNode.cachedNode.get(name);
+ if (temp == null) {
+ temp = getNodeFromBackend(createNew, currentNode, name);
+ }
+ }
+
+ currentNode = temp;
+ }
+ return currentNode;
+ }
+
+ private AbstractPreferences getNodeFromBackend(boolean createNew,
+ AbstractPreferences currentNode, String name)
+ throws BackingStoreException {
+ AbstractPreferences temp;
+ if (name.length() > MAX_NAME_LENGTH) {
+ // prefs.8=Name length is too long: {0}
+ throw new IllegalArgumentException(Messages.getString("prefs.8", //$NON-NLS-1$
+ name));
+ }
+ if (createNew) {
+ temp = currentNode.childSpi(name);
+ currentNode.cachedNode.put(name, temp);
+ if (temp.newNode && currentNode.nodeChangeListeners.size() > 0) {
+ currentNode.notifyChildAdded(temp);
+ }
+ } else {
+ temp = currentNode.getChild(name);
+ }
+ return temp;
+ }
+
+ @Override
+ public boolean nodeExists(String name) throws BackingStoreException {
+ AbstractPreferences startNode = null;
+ synchronized (lock) {
+ if (isRemoved()) {
+ if ("".equals(name)) { //$NON-NLS-1$
+ return false;
+ }
+ // prefs.9=This node has been removed\!
+ throw new IllegalStateException(Messages.getString("prefs.9")); //$NON-NLS-1$
+ }
+ validateName(name);
+ if ("".equals(name) || "/".equals(name)) { //$NON-NLS-1$ //$NON-NLS-2$
+ return true;
+ }
+ if (name.startsWith("/")) { //$NON-NLS-1$
+ startNode = root;
+ name = name.substring(1);
+ } else {
+ startNode = this;
+ }
+ }
+ try {
+ Preferences result = startNode.nodeImpl(name, false);
+ return null == result ? false : true;
+ } catch(IllegalArgumentException e) {
+ return false;
+ }
+ }
+
+ @Override
+ public Preferences parent() {
+ checkState();
+ return parentPref;
+ }
+
+ private void checkState() {
+ if (isRemoved()) {
+ // prefs.9=This node has been removed\!
+ throw new IllegalStateException(Messages.getString("prefs.9")); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ public void put(String key, String value) {
+ if (null == key || null == value) {
+ throw new NullPointerException();
+ }
+ if (key.length() > MAX_KEY_LENGTH || value.length() > MAX_VALUE_LENGTH) {
+ throw new IllegalArgumentException();
+ }
+ synchronized (lock) {
+ checkState();
+ putSpi(key, value);
+ }
+ notifyPreferenceChange(key, value);
+ }
+
+ @Override
+ public void putBoolean(String key, boolean value) {
+ String sval = String.valueOf(value);
+ put(key, sval);
+ }
+
+ @Override
+ public void putByteArray(String key, byte[] value) {
+ try {
+ put(key, Base64.encode(value, "US-ASCII")); //$NON-NLS-1$
+ } catch (UnsupportedEncodingException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public void putDouble(String key, double value) {
+ String sval = Double.toString(value);
+ put(key, sval);
+ }
+
+ @Override
+ public void putFloat(String key, float value) {
+ String sval = Float.toString(value);
+ put(key, sval);
+ }
+
+ @Override
+ public void putInt(String key, int value) {
+ String sval = Integer.toString(value);
+ put(key, sval);
+ }
+
+ @Override
+ public void putLong(String key, long value) {
+ String sval = Long.toString(value);
+ put(key, sval);
+ }
+
+ @Override
+ public void remove(String key) {
+ synchronized (lock) {
+ checkState();
+ removeSpi(key);
+ }
+ notifyPreferenceChange(key, null);
+ }
+
+ @Override
+ public void removeNode() throws BackingStoreException {
+ if (root == this) {
+ // prefs.A=Cannot remove root node\!
+ throw new UnsupportedOperationException(Messages.getString("prefs.A")); //$NON-NLS-1$
+ }
+ synchronized (parentPref.lock) {
+ removeNodeImpl();
+ }
+ }
+
+ private void removeNodeImpl() throws BackingStoreException {
+ synchronized (lock) {
+ checkState();
+ String[] childrenNames = childrenNamesSpi();
+ for (int i = 0; i < childrenNames.length; i++) {
+ if (null == cachedNode.get(childrenNames[i])) {
+ AbstractPreferences child = childSpi(childrenNames[i]);
+ cachedNode.put(childrenNames[i], child);
+ }
+ }
+ AbstractPreferences[] children = cachedNode
+ .values().toArray(new AbstractPreferences[0]);
+ for (int i = 0; i < children.length; i++) {
+ children[i].removeNodeImpl();
+ }
+ removeNodeSpi();
+ isRemoved = true;
+ parentPref.cachedNode.remove(nodeName);
+ }
+ if (parentPref.nodeChangeListeners.size() > 0) {
+ parentPref.notifyChildRemoved(this);
+ }
+ }
+
+ @Override
+ public void addNodeChangeListener(NodeChangeListener ncl) {
+ if (null == ncl) {
+ throw new NullPointerException();
+ }
+ checkState();
+ synchronized (nodeChangeListeners) {
+ nodeChangeListeners.add(ncl);
+ }
+ }
+
+ @Override
+ public void addPreferenceChangeListener(PreferenceChangeListener pcl) {
+ if (null == pcl) {
+ throw new NullPointerException();
+ }
+ checkState();
+ synchronized (preferenceChangeListeners) {
+ preferenceChangeListeners.add(pcl);
+ }
+ }
+
+ @Override
+ public void removeNodeChangeListener(NodeChangeListener ncl) {
+ checkState();
+ synchronized (nodeChangeListeners) {
+ int pos;
+ if ((pos = nodeChangeListeners.indexOf(ncl)) == -1) {
+ throw new IllegalArgumentException();
+ }
+ nodeChangeListeners.remove(pos);
+ }
+ }
+
+ @Override
+ public void removePreferenceChangeListener(PreferenceChangeListener pcl) {
+ checkState();
+ synchronized (preferenceChangeListeners) {
+ int pos;
+ if ((pos = preferenceChangeListeners.indexOf(pcl)) == -1) {
+ throw new IllegalArgumentException();
+ }
+ preferenceChangeListeners.remove(pos);
+ }
+ }
+
+ @Override
+ public void sync() throws BackingStoreException {
+ synchronized (lock) {
+ checkState();
+ syncSpi();
+ }
+ AbstractPreferences[] cc = cachedChildren();
+ int i;
+ for (i = 0; i < cc.length; i++) {
+ cc[i].sync();
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuffer sb = new StringBuffer();
+ sb.append(isUserNode() ? "User" : "System"); //$NON-NLS-1$ //$NON-NLS-2$
+ sb.append(" Preference Node: "); //$NON-NLS-1$
+ sb.append(absolutePath());
+ return sb.toString();
+ }
+
+ private void notifyChildAdded(Preferences child) {
+ NodeChangeEvent nce = new NodeAddEvent(this, child);
+ synchronized (events) {
+ events.add(nce);
+ events.notifyAll();
+ }
+ }
+
+ private void notifyChildRemoved(Preferences child) {
+ NodeChangeEvent nce = new NodeRemoveEvent(this, child);
+ synchronized (events) {
+ events.add(nce);
+ events.notifyAll();
+ }
+ }
+
+ private void notifyPreferenceChange(String key, String newValue) {
+ PreferenceChangeEvent pce = new PreferenceChangeEvent(this, key,
+ newValue);
+ synchronized (events) {
+ events.add(pce);
+ events.notifyAll();
+ }
+ }
+
+ private static class EventDispatcher extends Thread {
+ EventDispatcher(String name){
+ super(name);
+ }
+
+ @Override
+ public void run() {
+ while (true) {
+ EventObject event = null;
+ try {
+ event = getEventObject();
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ continue;
+ }
+ AbstractPreferences pref = (AbstractPreferences) event
+ .getSource();
+ if (event instanceof NodeAddEvent) {
+ dispatchNodeAdd((NodeChangeEvent) event,
+ pref.nodeChangeListeners);
+ } else if (event instanceof NodeRemoveEvent) {
+ dispatchNodeRemove((NodeChangeEvent) event,
+ pref.nodeChangeListeners);
+ } else if (event instanceof PreferenceChangeEvent) {
+ dispatchPrefChange((PreferenceChangeEvent) event,
+ pref.preferenceChangeListeners);
+ }
+ }
+ }
+
+ private EventObject getEventObject() throws InterruptedException {
+ synchronized (events) {
+ if (events.isEmpty()) {
+ events.wait();
+ }
+ EventObject event = events.get(0);
+ events.remove(0);
+ return event;
+ }
+ }
+
+ private void dispatchPrefChange(PreferenceChangeEvent event,
+ List<EventListener> preferenceChangeListeners) {
+ synchronized (preferenceChangeListeners) {
+ Iterator<EventListener> i = preferenceChangeListeners.iterator();
+ while (i.hasNext()) {
+ PreferenceChangeListener pcl = (PreferenceChangeListener) i
+ .next();
+ pcl.preferenceChange(event);
+ }
+ }
+ }
+
+ private void dispatchNodeRemove(NodeChangeEvent event,
+ List<EventListener> nodeChangeListeners) {
+ synchronized (nodeChangeListeners) {
+ Iterator<EventListener> i = nodeChangeListeners.iterator();
+ while (i.hasNext()) {
+ NodeChangeListener ncl = (NodeChangeListener) i.next();
+ ncl.childRemoved(event);
+ }
+ }
+ }
+
+ private void dispatchNodeAdd(NodeChangeEvent event,
+ List<EventListener> nodeChangeListeners) {
+ synchronized (nodeChangeListeners) {
+ Iterator<EventListener> i = nodeChangeListeners.iterator();
+ while (i.hasNext()) {
+ NodeChangeListener ncl = (NodeChangeListener) i.next();
+ ncl.childAdded(event);
+ }
+ }
+ }
+ }
+
+ private static class NodeAddEvent extends NodeChangeEvent {
+ //The base class is NOT serializable, so this class isn't either.
+ private static final long serialVersionUID = 1L;
+
+ public NodeAddEvent(Preferences p, Preferences c) {
+ super(p, c);
+ }
+ }
+
+ private static class NodeRemoveEvent extends NodeChangeEvent {
+ //The base class is NOT serializable, so this class isn't either.
+ private static final long serialVersionUID = 1L;
+
+ public NodeRemoveEvent(Preferences p, Preferences c) {
+ super(p, c);
+ }
+ }
+}
diff --git a/prefs/src/main/java/java/util/prefs/BackingStoreException.java b/prefs/src/main/java/java/util/prefs/BackingStoreException.java
new file mode 100644
index 0000000..7ddb3f3
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/BackingStoreException.java
@@ -0,0 +1,53 @@
+/*
+ * 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 java.util.prefs;
+
+
+/**
+ * An exception to indicate that some error was encountered while accessing
+ * the backing store.
+ *
+ * @since 1.4
+ */
+public class BackingStoreException extends Exception {
+
+ private static final long serialVersionUID = 859796500401108469L;
+
+ /**
+ * Constructs a new <code>BackingStoreException</code> instance using an
+ * exception message.
+ *
+ * @param s the exception message.
+ */
+ public BackingStoreException (String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs a new <code>BackingStoreException</code> instance using a
+ * nested <code>Throwable</code> instance.
+ *
+ * @param t the nested <code>Throwable</code> instance.
+ */
+ public BackingStoreException (Throwable t) {
+ super(t);
+ }
+}
+
+
+
diff --git a/prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java b/prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java
new file mode 100644
index 0000000..830cf32
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/FilePreferencesFactoryImpl.java
@@ -0,0 +1,44 @@
+/* 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 java.util.prefs;
+
+/**
+ * Default implementation of <code>PreferencesFactory</code> for Linux
+ * platform, using file system as back end.
+ *
+ * @since 1.4
+ */
+class FilePreferencesFactoryImpl implements PreferencesFactory {
+ // user root preferences
+ private static final Preferences USER_ROOT = new FilePreferencesImpl(true);
+
+ // system root preferences
+ private static final Preferences SYSTEM_ROOT = new FilePreferencesImpl(false);
+
+ public FilePreferencesFactoryImpl() {
+ super();
+ }
+
+ public Preferences userRoot() {
+ return USER_ROOT;
+ }
+
+ public Preferences systemRoot() {
+ return SYSTEM_ROOT;
+ }
+
+}
diff --git a/prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java b/prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java
new file mode 100644
index 0000000..7394cec
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/FilePreferencesImpl.java
@@ -0,0 +1,236 @@
+/* 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 java.util.prefs;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.harmony.prefs.internal.nls.Messages;
+
+/**
+ * Default implementation of <code>AbstractPreferences</code> for Linux platform,
+ * using file system as back end.
+ *
+ * TODO some sync mechanism with backend, Performance - check file edit date
+ *
+ * @since 1.4
+ */
+class FilePreferencesImpl extends AbstractPreferences {
+
+ /*
+ * --------------------------------------------------------------
+ * Class fields
+ * --------------------------------------------------------------
+ */
+
+ //prefs file name
+ private static final String PREFS_FILE_NAME = "prefs.xml"; //$NON-NLS-1$
+
+ //home directory for user prefs
+ private static String USER_HOME;
+
+ //home directory for system prefs
+ private static String SYSTEM_HOME;
+
+ /*
+ * --------------------------------------------------------------
+ * Class initializer
+ * --------------------------------------------------------------
+ */
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ USER_HOME = System.getProperty("user.home") + "/.java/.userPrefs";//$NON-NLS-1$ //$NON-NLS-2$
+ SYSTEM_HOME = System.getProperty("java.home") + "/.systemPrefs";//$NON-NLS-1$//$NON-NLS-2$
+ return null;
+ }
+
+ });
+ }
+
+ /*
+ * --------------------------------------------------------------
+ * Instance fields
+ * --------------------------------------------------------------
+ */
+
+ //file path for this preferences node
+ private String path;
+
+ //internal cache for prefs key-value pair
+ private Properties prefs;
+
+ //file represents this preferences node
+ private File prefsFile;
+
+ //parent dir for this preferences node
+ private File dir;
+
+ //cache for removed prefs key-value pair
+ private Set<String> removed = new HashSet<String>();
+
+ //cache for updated prefs key-value pair
+ private Set<String> updated = new HashSet<String>();
+
+ /*
+ * --------------------------------------------------------------
+ * Constructors
+ * --------------------------------------------------------------
+ */
+
+ /**
+ * Construct root <code>FilePreferencesImpl</code> instance, construct
+ * user root if userNode is true, system root otherwise
+ */
+ FilePreferencesImpl(boolean userNode) {
+ super(null, ""); //$NON-NLS-1$
+ this.userNode = userNode;
+ path = userNode ? USER_HOME : SYSTEM_HOME;
+ initPrefs();
+ }
+
+ /**
+ * Construct a prefs using given parent and given name
+ */
+ private FilePreferencesImpl(AbstractPreferences parent, String name) {
+ super(parent, name);
+ path = ((FilePreferencesImpl) parent).path + File.separator + name;
+ initPrefs();
+ }
+
+ private void initPrefs() {
+ dir = new File(path);
+ newNode = (AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return Boolean.valueOf(!dir.exists());
+ }
+ })).booleanValue();
+ prefsFile = new File(path + File.separator + PREFS_FILE_NAME);
+ prefs = XMLParser.loadFilePrefs(prefsFile);
+ }
+
+ @Override
+ protected String[] childrenNamesSpi() throws BackingStoreException {
+ String[] names = AccessController
+ .doPrivileged(new PrivilegedAction<String[]>() {
+ public String[] run() {
+ return dir.list(new FilenameFilter() {
+ public boolean accept(File parent, String name) {
+ return new File(path + File.separator + name).isDirectory();
+ }
+ });
+
+ }
+ });
+ if (null == names) {// file is not a directory, exception case
+ // prefs.3=Cannot get children names for {0}!
+ throw new BackingStoreException(
+ Messages.getString("prefs.3", toString())); //$NON-NLS-1$
+ }
+ return names;
+ }
+
+ @Override
+ protected AbstractPreferences childSpi(String name) {
+ FilePreferencesImpl child = new FilePreferencesImpl(this, name);
+ return child;
+ }
+
+ @Override
+ protected void flushSpi() throws BackingStoreException {
+ try {
+ //if removed, return
+ if(isRemoved()){
+ return;
+ }
+ // reload
+ Properties currentPrefs = XMLParser.loadFilePrefs(prefsFile);
+ // merge
+ Iterator<String> it = removed.iterator();
+ while (it.hasNext()) {
+ currentPrefs.remove(it.next());
+ }
+ removed.clear();
+ it = updated.iterator();
+ while (it.hasNext()) {
+ Object key = it.next();
+ currentPrefs.put(key, prefs.get(key));
+ }
+ updated.clear();
+ // flush
+ prefs = currentPrefs;
+ XMLParser.flushFilePrefs(prefsFile, prefs);
+ } catch (Exception e) {
+ throw new BackingStoreException(e);
+ }
+ }
+
+ @Override
+ protected String getSpi(String key) {
+ try {
+ if (null == prefs) {
+ prefs = XMLParser.loadFilePrefs(prefsFile);
+ }
+ return prefs.getProperty(key);
+ } catch (Exception e) {// if Exception happened, return null
+ return null;
+ }
+ }
+
+ @Override
+ protected String[] keysSpi() throws BackingStoreException {
+ return prefs.keySet().toArray(new String[0]);
+ }
+
+ @Override
+ protected void putSpi(String name, String value) {
+ prefs.setProperty(name, value);
+ updated.add(name);
+ }
+
+ @Override
+ protected void removeNodeSpi() throws BackingStoreException {
+ boolean removeSucceed = (AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ prefsFile.delete();
+ return Boolean.valueOf(dir.delete());
+ }
+ })).booleanValue();
+ if (!removeSucceed) {
+ // prefs.4=Cannot remove {0}!
+ throw new BackingStoreException(Messages.getString("prefs.4", toString())); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ protected void removeSpi(String key) {
+ prefs.remove(key);
+ updated.remove(key);
+ removed.add(key);
+ }
+
+ @Override
+ protected void syncSpi() throws BackingStoreException {
+ flushSpi();
+ }
+}
diff --git a/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java b/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java
new file mode 100644
index 0000000..ac79e2d
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java
@@ -0,0 +1,66 @@
+/*
+ * 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 java.util.prefs;
+
+/**
+ * An exception to indicate that the input XML file is not well-formed or
+ * validating to the appropriate document type, which is specified by
+ * <code>Preferences</code>.
+ *
+ * @see Preferences
+ *
+ * @since 1.4
+ */
+public class InvalidPreferencesFormatException extends Exception {
+
+ private static final long serialVersionUID = -791715184232119669L;
+
+ /**
+ * Constructs a new <code>InvalidPreferencesFormatException</code> instance using an
+ * exception message.
+ *
+ * @param s the exception message.
+ */
+ public InvalidPreferencesFormatException (String s) {
+ super(s);
+ }
+
+ /**
+ * Constructs a new <code>InvalidPreferencesFormatException</code> instance using a
+ * exception message and a nested <code>Throwable</code> instance.
+ *
+ * @param s the exception message.
+ * @param t the nested <code>Throwable</code> instance.
+ */
+ public InvalidPreferencesFormatException (String s, Throwable t) {
+ super(s,t);
+ }
+
+ /**
+ * Constructs a new <code>InvalidPreferencesFormatException</code> instance using a
+ * nested <code>Throwable</code> instance.
+ *
+ * @param t the nested <code>Throwable</code> instance.
+ */
+ public InvalidPreferencesFormatException (Throwable t) {
+ super(t);
+ }
+}
+
+
+
diff --git a/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java b/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java
new file mode 100644
index 0000000..d41fbd2
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java
@@ -0,0 +1,97 @@
+/* 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 java.util.prefs;
+
+import java.io.Serializable;
+import java.util.EventObject;
+import java.io.ObjectOutputStream;
+import java.io.ObjectInputStream;
+import java.io.NotSerializableException;
+import java.io.IOException;
+
+/**
+ * This is the event class to indicate one child of the preferences node has
+ * been added or deleted.
+ * <p>
+ * Please note that this class cannot be serialized actually, so relevant
+ * serialization methods only throw <code>NotSerializableException</code>.</p>
+ *
+ * @see java.util.prefs.Preferences
+ * @see java.util.prefs.NodeChangeListener
+ *
+ * @since 1.4
+ */
+public class NodeChangeEvent extends EventObject implements Serializable {
+
+ private static final long serialVersionUID = 8068949086596572957L;
+
+ private final Preferences parent;
+ private final Preferences child;
+
+ /**
+ * Construct a new <code>NodeChangeEvent</code> instance.
+ *
+ * @param p the <code>Preferences</code> instance that this event happened,
+ * this object is considered as event's source.
+ * @param c the child <code>Preferences</code> instance that was added
+ * or deleted.
+ */
+ public NodeChangeEvent (Preferences p, Preferences c) {
+ super(p);
+ parent = p;
+ child = c;
+ }
+
+ /**
+ * Get the <code>Preferences</code> instance that this event happened.
+ *
+ * @return the <code>Preferences</code> instance that this event happened.
+ */
+ public Preferences getParent() {
+ return parent;
+ }
+
+ /**
+ * Get the child <code>Preferences</code> node that was added or removed.
+ *
+ * @return the child <code>Preferences</code> node that was added or removed.
+ */
+ public Preferences getChild() {
+ return child;
+ }
+
+ /*
+ * This method always throws a <code>NotSerializableException</code>, because
+ * this object cannot be serialized,
+ */
+ private void writeObject (ObjectOutputStream out) throws IOException {
+ throw new NotSerializableException();
+ }
+
+ /*
+ * This method always throws a <code>NotSerializableException</code>, because
+ * this object cannot be serialized,
+ */
+ private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException {
+ throw new NotSerializableException();
+ }
+}
+
+
+
+
diff --git a/prefs/src/main/java/java/util/prefs/NodeChangeListener.java b/prefs/src/main/java/java/util/prefs/NodeChangeListener.java
new file mode 100644
index 0000000..032fc16
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/NodeChangeListener.java
@@ -0,0 +1,49 @@
+/* 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 java.util.prefs;
+
+import java.util.EventListener;
+import java.util.prefs.NodeChangeEvent;
+
+/**
+ * This interface is used to handle preferences node change event. Implementation
+ * of this interface can be installed by <code>Preferences</code> instance.
+ *
+ * @see Preferences
+ * @see NodeChangeEvent
+ *
+ * @since 1.4
+ */
+public interface NodeChangeListener extends EventListener {
+ /**
+ * This method gets called whenever a child is added to a node.
+ *
+ * @param e Node change event.
+ */
+ public void childAdded (NodeChangeEvent e);
+
+ /**
+ * This method gets called whenever a child is removed from a node.
+ *
+ * @param e Node change event.
+ */
+ public void childRemoved (NodeChangeEvent e);
+}
+
+
+
diff --git a/prefs/src/main/java/java/util/prefs/NodeSet.java b/prefs/src/main/java/java/util/prefs/NodeSet.java
new file mode 100644
index 0000000..202b90b
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/NodeSet.java
@@ -0,0 +1,34 @@
+package java.util.prefs;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+class NodeSet implements NodeList {
+
+ ArrayList<Node> list = new ArrayList<Node>();
+
+ public NodeSet(Iterator<Node> nodes) {
+ while(nodes.hasNext()) {
+ list.add(nodes.next());
+ }
+ }
+
+ public int getLength() {
+ return list.size();
+ }
+
+ public Node item(int index) {
+ Node result = null;
+ try {
+ result = list.get(index);
+ } catch(IndexOutOfBoundsException ioobe) {
+ // TODO log this event?
+ return null;
+ }
+
+ return result;
+ }
+}
diff --git a/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java b/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java
new file mode 100644
index 0000000..ff3f9a2
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java
@@ -0,0 +1,112 @@
+/* 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 java.util.prefs;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.util.EventObject;
+
+/**
+ * This is the event class to indicate some preferences has been added,
+ * deleted or updated.
+ * <p>
+ * Please note that this class cannot be serialized actually, so relevant
+ * serialization methods only throw <code>NotSerializableException</code>.
+ * </p>
+ *
+ * @see java.util.prefs.Preferences
+ * @see java.util.prefs.PreferenceChangeListener
+ *
+ * @since 1.4
+ */
+public class PreferenceChangeEvent extends EventObject implements Serializable {
+
+ private static final long serialVersionUID = 793724513368024975L;
+
+ private final Preferences node;
+
+ private final String key;
+
+ private final String value;
+
+ /**
+ * Construct a new <code>PreferenceChangeEvent</code> instance.
+ *
+ * @param p the <code>Preferences</code> instance that this event happened,
+ * this object is considered as event's source.
+ * @param k the changed preference's key
+ * @param v the new value of the changed preference, this value can be null,
+ * which means the preference is removed.
+ */
+ public PreferenceChangeEvent(Preferences p, String k, String v) {
+ super(p);
+ node = p;
+ key = k;
+ value = v;
+ }
+
+ /**
+ * Get the changed preference's key.
+ *
+ * @return the changed preference's key
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Get the new value of the changed preference, or null if this preference
+ * is removed.
+ *
+ * @return the new value of the changed preference, or null if this preference
+ * is removed.
+ */
+ public String getNewValue() {
+ return value;
+ }
+
+ /**
+ * Get the <code>Preferences</code> instance that this event happened.
+ *
+ * @return the <code>Preferences</code> instance that this event happened.
+ */
+ public Preferences getNode() {
+ return node;
+ }
+
+ /*
+ * This method always throws a <code>NotSerializableException</code>, because
+ * this object cannot be serialized,
+ */
+ private void writeObject(ObjectOutputStream out) throws IOException {
+ throw new NotSerializableException();
+ }
+
+ /*
+ * This method always throws a <code>NotSerializableException</code>, because
+ * this object cannot be serialized,
+ */
+ private void readObject(ObjectInputStream in) throws IOException{
+ throw new NotSerializableException();
+ }
+}
+
+
diff --git a/prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java b/prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java
new file mode 100644
index 0000000..2ddb169
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/PreferenceChangeListener.java
@@ -0,0 +1,45 @@
+/* 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 java.util.prefs;
+
+import java.util.EventListener;
+
+/**
+ * This interface is used to handle preferences change event. Implementation
+ * of this interface can be installed by <code>Preferences</code> instance.
+ *
+ * @see Preferences
+ * @see PreferenceChangeEvent
+ *
+ *
+ * @since 1.4
+ */
+public interface PreferenceChangeListener extends EventListener {
+
+ /**
+ * This method gets invoked whenever some preference is added, deleted or
+ * updated.
+ *
+ * @param pce the event instance which describes the changed Preferences
+ * instance and preferences value.
+ */
+ void preferenceChange (PreferenceChangeEvent pce);
+}
+
+
+
diff --git a/prefs/src/main/java/java/util/prefs/Preferences.java b/prefs/src/main/java/java/util/prefs/Preferences.java
new file mode 100644
index 0000000..d82ad1e
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/Preferences.java
@@ -0,0 +1,933 @@
+/* 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 java.util.prefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.Enumeration;
+
+import org.apache.harmony.prefs.internal.nls.Messages;
+
+/**
+ * <code>Preferences</code> instance represents one node in preferences tree,
+ * which provide a mechanisms to store and access configuration data in a
+ * hierarchical way. Two hierarchy tree is maintained, one for system
+ * preferences shared by all users, and the other for user preferences which is
+ * specific for each user. Preferences hierarchy tree and data is stored
+ * precisely in implementation-dependent backend, and user doesn't need to care
+ * about the details.
+ * <p>
+ * Every node has one name and one unique absolute path in a similar way with
+ * directories in file system. The root node's name is "", and other nodes' name
+ * string cannot contains slash and cannot be empty. The root node's absolute
+ * path is "/", and other nodes' absolute path equals &lt;parent's absolute
+ * path&gt; + "/" + &lt;node's name&gt;. All absolute paths start with slash.
+ * Every node has one relative path to one of its ancestor. Relative path
+ * doesn't start with slash, and equals to absolute path when following after
+ * ancestor's absolute path and a slash.
+ * </p>
+ * <p>
+ * The modification to preferences data may be asynchronous, which means they
+ * may don't block and may returns immediately, implementation can feel free to
+ * the modifications to the backend in any time until the flush() or sync()
+ * method is invoked, these two methods force synchronized updates to backend.
+ * Please note that if JVM exit normally, the implementation must assure all
+ * modifications are persisted implicitly.
+ * </p>
+ * <p>
+ * User invoking methods that retrieve preferences must provide default value,
+ * default value is returned when preferences cannot be found or backend is
+ * unavailable. Some other methods will throw <code>BackingStoreException</code>
+ * when backend is unavailable.
+ * </p>
+ * <p>
+ * Preferences can be export to/import from XML files, the XML document must
+ * have the following DOCTYPE declaration:
+ * </p>
+ * <p>
+ * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
+ * </p>
+ * <p>
+ * This system URI is not really accessed by network, it is only a
+ * identification string. Visit the DTD location to see the actual format
+ * permitted.
+ * </p>
+ * <p>
+ * There has to be a concrete <code>PreferencesFactory</code> type for every
+ * concrete <code>Preferences</code> type developed. Every J2SE implementation
+ * must provide a default implementation for every supported platform, and the
+ * default implementation can be replaced in some way. This implementation uses
+ * system property java.util.prefs.PreferencesFactory to dictate the preferences
+ * implementation.
+ * </p>
+ * <p>
+ * Methods of this class is thread-safe. If multi JVMs using same backend
+ * concurrently, the backend won't be corrupted, but no other guarantees is
+ * made.
+ * </p>
+ *
+ * @see PreferencesFactory
+ *
+ * @since 1.4
+ */
+public abstract class Preferences {
+
+ /*
+ * ---------------------------------------------------------
+ * Class fields
+ * ---------------------------------------------------------
+ */
+
+ /**
+ * Maximum size in characters of preferences key
+ */
+ public static final int MAX_KEY_LENGTH = 80;
+
+ /**
+ * Maximum size in characters of preferences name
+ */
+ public static final int MAX_NAME_LENGTH = 80;
+
+ /**
+ * Maximum size in characters of preferences value
+ */
+ public static final int MAX_VALUE_LENGTH = 8192;
+
+ // BEGIN android-added
+ /**
+ * the name of the configuration file where preferences factory class names
+ * can be specified.
+ */
+ private static final String FACTORY_CONFIGURATION_FILE_NAME = "META-INF/services/java.util.prefs.PreferencesFactory"; //$NON-NLS-1$
+
+ /**
+ * the encoding of configuration files
+ */
+ private static final String CONFIGURATION_FILE_ENCODING = "UTF-8"; //$NON-NLS-1$
+
+ /**
+ * the comment string used in configuration files
+ */
+ private static final String CONFIGURATION_FILE_COMMENT = "#"; //$NON-NLS-1$
+ // END android-added
+
+ //permission
+ private static final RuntimePermission PREFS_PERM = new RuntimePermission("preferences"); //$NON-NLS-1$
+
+ //factory used to get user/system prefs root
+ private static final PreferencesFactory factory;
+
+ /**
+ * ---------------------------------------------------------
+ * Class initializer
+ * ---------------------------------------------------------
+ */
+ static{
+ String factoryClassName = AccessController.doPrivileged(new PrivilegedAction<String>() {
+ public String run() {
+ return System.getProperty("java.util.prefs.PreferencesFactory"); //$NON-NLS-1$
+ }
+ });
+ // BEGIN android-removed
+ // if(factoryClassName != null) {
+ // try {
+ // ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ // if(loader == null){
+ // loader = ClassLoader.getSystemClassLoader();
+ // }
+ // Class<?> factoryClass = loader.loadClass(factoryClassName);
+ // factory = (PreferencesFactory) factoryClass.newInstance();
+ // } catch (Exception e) {
+ // // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1}
+ // throw new InternalError(Messages.getString("prefs.10", factoryClassName, e)); //$NON-NLS-1$
+ // }
+ // }
+ // END android-removed
+ // BEGIN android-added
+ ClassLoader loader = Thread.currentThread().getContextClassLoader();
+ if(loader == null){
+ loader = ClassLoader.getSystemClassLoader();
+ }
+ if(factoryClassName == null) {
+ Enumeration<URL> en = null;
+ try {
+ en = loader.getResources(FACTORY_CONFIGURATION_FILE_NAME);
+ BufferedReader reader = null;
+ int commentIndex = 0;
+ while(en.hasMoreElements()) {
+ try {
+ InputStream is = en.nextElement().openStream();
+ // Read each line for charset provider class names
+ // BEGIN android-modified
+ reader = new BufferedReader(new InputStreamReader(is,
+ CONFIGURATION_FILE_ENCODING), 8192);
+ // END android-modified
+ factoryClassName = reader.readLine();
+ commentIndex = factoryClassName
+ .indexOf(CONFIGURATION_FILE_COMMENT);
+ if(commentIndex > 0) {
+ factoryClassName = factoryClassName.substring(0,
+ commentIndex).trim();
+ }
+ if(factoryClassName.length() > 0) {
+ break;
+ }
+ } catch (IOException ex) {
+ // ignore if a resource couldn't be read
+ }
+ }
+ } catch (Exception e) {
+ // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1}
+ throw new InternalError(Messages.getString("prefs.10",
+ FACTORY_CONFIGURATION_FILE_NAME, e)); //$NON-NLS-1$
+ }
+ }
+
+ if(factoryClassName == null) {
+ factoryClassName = "java.util.prefs.FilePreferencesFactoryImpl";
+ }
+
+ try {
+ Class<?> c = loader.loadClass(factoryClassName);
+ factory = (PreferencesFactory) c.newInstance();
+ } catch(Exception e) {
+ // prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1}
+ throw new InternalError(Messages.getString("prefs.10",
+ factoryClassName, e)); //$NON-NLS-1$
+ }
+ // END android-added
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * Constructors
+ * ---------------------------------------------------------
+ */
+
+ /**
+ * Default constructor, for use by subclasses only.
+ */
+ protected Preferences() {
+ super();
+ }
+
+ /*
+ * ---------------------------------------------------------
+ * Methods
+ * ---------------------------------------------------------
+ */
+
+ /**
+ * Get this preference node's absolute path string.
+ *
+ * @return this preference node's absolute path string.
+ */
+ public abstract String absolutePath();
+
+ /**
+ * Return names of all children of this node, or empty string if this node
+ * has no children.
+ *
+ * @return names of all children of this node
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract String[] childrenNames() throws BackingStoreException;
+
+ /**
+ * Remove all preferences of this node.
+ *
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void clear() throws BackingStoreException;
+
+ /**
+ * Export all preferences of this node to the given output stream in XML
+ * document.
+ * <p>
+ * This XML document has the following DOCTYPE declaration:
+ * <pre>
+ * &lt;!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"&gt;</pre>
+ * And the UTF-8 encoding will be used. Please note that this node is not
+ * thread-safe, which is an exception of this class.
+ * </p>
+ * @param ostream
+ * the output stream to export the XML
+ * @throws IOException
+ * if export operation caused an <code>IOException</code>
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void exportNode (OutputStream ostream) throws IOException, BackingStoreException;
+
+ /**
+ * Export all preferences of this node and its all descendants to the given
+ * output stream in XML document.
+ * <p>
+ * This XML document has the following DOCTYPE declaration:
+ * <pre>
+ * &lt;!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"&gt;</pre> *
+ * And the UTF-8 encoding will be used. Please note that this node is not
+ * thread-safe, which is an exception of this class.
+ * </p>
+ * @param ostream
+ * the output stream to export the XML
+ * @throws IOException
+ * if export operation caused an <code>IOException</code>
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void exportSubtree (OutputStream ostream) throws IOException, BackingStoreException;
+
+ /**
+ * Force the updates to this node and its descendants to the backing store.
+ * <p>
+ * If this node has been removed, then the invocation of this method only
+ * flush this node without descendants.
+ * </p>
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ public abstract void flush() throws BackingStoreException;
+
+ /**
+ * Return the string value mapped to the given key, or default value if no
+ * value is mapped or backing store is unavailable.
+ * <p>
+ * Some implementations may store default values in backing stores. In this case,
+ * if there is no value mapped to the given key, the stored default value is
+ * returned.
+ * </p>
+ *
+ * @param key the preference key
+ * @param deflt the default value, which will be returned if no value is
+ * mapped to the given key or backing store unavailable
+ * @return the preference value mapped to the given key, or default value if
+ * no value is mapped or backing store unavailable
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws NullPointerException
+ * if parameter key is null
+ */
+ public abstract String get (String key, String deflt);
+
+ /**
+ * Return the boolean value mapped to the given key, or default value if no
+ * value is mapped, backing store is unavailable, or the value is invalid.
+ * <p>
+ * The valid value is string equals "true", which represents true, or "false",
+ * which represents false, case is ignored.
+ * </p>
+ * <p>
+ * Some implementations may store default values in backing stores. In this case,
+ * if there is no value mapped to the given key, the stored default value is
+ * returned.
+ * </p>
+ *
+ * @param key the preference key
+ * @param deflt the default value, which will be returned if no value is
+ * mapped to the given key, backing store unavailable or value
+ * is invalid
+ * @return the boolean value mapped to the given key, or default value if
+ * no value is mapped, backing store unavailable or value is invalid
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws NullPointerException
+ * if parameter key is null
+ */
+ public abstract boolean getBoolean (String key, boolean deflt);
+
+ /**
+ * Return the byte array value mapped to the given key, or default value if no
+ * value is mapped, backing store is unavailable, or the value is invalid string.
+ * <p>
+ * The valid value string is Base64 encoded binary data. The Base64 encoding
+ * is as defined in <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>,
+ * section 6.8.
+ * </p>
+ * <p>
+ * Some implementations may store default values in backing stores. In this case,
+ * if there is no value mapped to the given key, the stored default value is
+ * returned.
+ * </p>
+ *
+ * @param key the preference key
+ * @param deflt the default value, which will be returned if no value is
+ * mapped to the given key, backing store unavailable or value
+ * is invalid
+ * @return the byte array value mapped to the given key, or default value if
+ * no value is mapped, backing store unavailable or value is invalid
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws NullPointerException
+ * if parameter key is null
+ */
+ public abstract byte[] getByteArray (String key, byte[] deflt);
+
+ /**
+ * Return the double value mapped to the given key, or default value if no
+ * value is mapped, backing store is unavailable, or the value is invalid string.
+ * <p>
+ * The valid value string can be converted to double number by
+ * {@link Double#parseDouble(String) Double.parseDouble(String)}.
+ * </p>
+ * <p>
+ * Some implementations may store default values in backing stores. In this case,
+ * if there is no value mapped to the given key, the stored default value is
+ * returned.
+ * </p>
+ *
+ * @param key the preference key
+ * @param deflt the default value, which will be returned if no value is
+ * mapped to the given key, backing store unavailable or value
+ * is invalid
+ * @return the double value mapped to the given key, or default value if
+ * no value is mapped, backing store unavailable or value is invalid
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws NullPointerException
+ * if parameter key is null
+ */
+ public abstract double getDouble (String key, double deflt);
+
+ /**
+ * Return the float value mapped to the given key, or default value if no
+ * value is mapped, backing store is unavailable, or the value is invalid string.
+ * <p>
+ * The valid value string can be converted to float number by
+ * {@link Float#parseFloat(String) Float.parseFloat(String)}.
+ * </p>
+ * <p>
+ * Some implementations may store default values in backing stores. In this case,
+ * if there is no value mapped to the given key, the stored default value is
+ * returned.
+ * </p>
+ *
+ * @param key the preference key
+ * @param deflt the default value, which will be returned if no value is
+ * mapped to the given key, backing store unavailable or value
+ * is invalid
+ * @return the float value mapped to the given key, or default value if
+ * no value is mapped, backing store unavailable or value is invalid
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws NullPointerException
+ * if parameter key is null
+ */
+ public abstract float getFloat (String key, float deflt);
+
+ /**
+ * Return the float value mapped to the given key, or default value if no
+ * value is mapped, backing store is unavailable, or the value is invalid string.
+ * <p>
+ * The valid value string can be converted to integer by
+ * {@link Integer#parseInt(String) Integer.parseInt(String)}.
+ * </p>
+ * <p>
+ * Some implementations may store default values in backing stores. In this case,
+ * if there is no value mapped to the given key, the stored default value is
+ * returned.
+ * </p>
+ *
+ * @param key the preference key
+ * @param deflt the default value, which will be returned if no value is
+ * mapped to the given key, backing store unavailable or value
+ * is invalid
+ * @return the integer value mapped to the given key, or default value if
+ * no value is mapped, backing store unavailable or value is invalid
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws NullPointerException
+ * if parameter key is null
+ */
+ public abstract int getInt (String key, int deflt);
+
+ /**
+ * Return the long value mapped to the given key, or default value if no
+ * value is mapped, backing store is unavailable, or the value is invalid string.
+ * <p>
+ * The valid value string can be converted to long integer by
+ * {@link Long#parseLong(String) Long.parseLong(String)}.
+ * </p>
+ * <p>
+ * Some implementations may store default values in backing stores. In this case,
+ * if there is no value mapped to the given key, the stored default value is
+ * returned.
+ * </p>
+ *
+ * @param key the preference key
+ * @param deflt the default value, which will be returned if no value is
+ * mapped to the given key, backing store unavailable or value
+ * is invalid
+ * @return the long value mapped to the given key, or default value if
+ * no value is mapped, backing store unavailable or value is invalid
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws NullPointerException
+ * if parameter key is null
+ */
+ public abstract long getLong (String key, long deflt);
+
+ /**
+ * Import all preferences from the given input stream in XML document.
+ * <p>
+ * This XML document has the following DOCTYPE declaration:
+ * <pre>
+ * &lt;!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"&gt;</pre> *
+ * Please note that this node is not thread-safe, which is an exception of
+ * this class.
+ * </p>
+ *
+ * @param istream
+ * the given input stream to read data
+ * @throws InvalidPreferencesFormatException
+ * if the data read from given input stream is not valid XML
+ * document
+ * @throws IOException
+ * if import operation caused an <code>IOException</code>
+ * @throws SecurityException
+ * if <code>RuntimePermission("preferences")</code> is denied
+ * by a <code>SecurityManager</code>
+ */
+ public static void importPreferences (InputStream istream) throws InvalidPreferencesFormatException, IOException {
+ checkSecurity();
+ if(null == istream){
+ // prefs.0=Inputstream cannot be null\!
+ throw new MalformedURLException(Messages.getString("prefs.0")); //$NON-NLS-1$
+ }
+ XMLParser.importPrefs(istream);
+ }
+
+ /**
+ * Return true if this is a user preferences, false if this is a system
+ * preferences
+ *
+ * @return true if this is a user preferences, false if this is a
+ * system preferences
+ */
+ public abstract boolean isUserNode();
+
+ /**
+ * Return all preferences keys stored in this node, or empty array if no
+ * key is found.
+ *
+ * @return all preferences keys in this node
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract String[] keys() throws BackingStoreException;
+
+ /**
+ * Return name of this node.
+ *
+ * @return the name of this node
+ */
+ public abstract String name();
+
+ /**
+ * Return the preferences node with the given path name. The path name can
+ * be relative or absolute. The dictated preferences and its ancestors will
+ * be created if they do not exist.
+ * <p>
+ * The path is treated as relative to this node if it doesn't start with
+ * slash, or as absolute otherwise.</p>
+ *
+ * @param path the path name of dictated preferences
+ * @return the dictated preferences node
+ * @throws IllegalStateException
+ * if this node has been removed.
+ * @throws IllegalArgumentException
+ * if the path name is invalid.
+ * @throws NullPointerException
+ * if given path is null.
+ */
+ public abstract Preferences node (String path);
+
+ /**
+ * Return the preferences node with the given path name. The path is treated
+ * as relative to this node if it doesn't start with slash, or as absolute
+ * otherwise.
+ * <p>
+ * Please note that if this node has been removed, invocation of this node
+ * will throw <code>IllegalStateException</code> except the given path is
+ * empty string, which will return false.
+ * </p>
+ *
+ * @param path the path name of dictated preferences
+ * @return true if the dictated preferences node exists
+ * @throws IllegalStateException
+ * if this node has been removed and the path is not empty string.
+ * @throws IllegalArgumentException
+ * if the path name is invalid.
+ * @throws NullPointerException
+ * if given path is null.
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ */
+ public abstract boolean nodeExists (String path) throws BackingStoreException;
+
+ /**
+ * Return the parent preferences node of this node, or null if this node is root.
+ *
+ * @return the parent preferences node of this node.
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract Preferences parent();
+
+ /**
+ * Add new preferences to this node using given key and value, or update
+ * value if preferences with given key has already existed.
+ *
+ * @param key the preferences key to be added or be updated
+ * @param value the preferences value for the given key
+ * @throws NullPointerException
+ * if the given key or value is null
+ * @throws IllegalArgumentException
+ * if the given key's length is bigger than
+ * <code>MAX_KEY_LENGTH</code>, or the value's length is bigger
+ * than <code>MAX_VALUE_LENGTH</code>
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void put (String key, String value);
+
+ /**
+ * Add new preferences to this node using given key and string form of given
+ * value, or update value if preferences with given key has already existed.
+ *
+ * @param key the preferences key to be added or be updated
+ * @param value the preferences value for the given key
+ * @throws NullPointerException
+ * if the given key is null
+ * @throws IllegalArgumentException
+ * if the given key's length is bigger than
+ * <code>MAX_KEY_LENGTH</code>
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void putBoolean (String key, boolean value);
+
+ /**
+ * Add new preferences to this node using given key and string form of given
+ * value, or update value if preferences with given key has already existed.
+ * <p>
+ * The string form of value is the Base64 encoded binary data of the given
+ * byte array. The Base64 encoding is as defined in
+ * <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>, section 6.8.</p>
+ *
+ * @param key the preferences key to be added or be updated
+ * @param value the preferences value for the given key
+ * @throws NullPointerException
+ * if the given key or value is null
+ * @throws IllegalArgumentException
+ * if the given key's length is bigger than
+ * <code>MAX_KEY_LENGTH</code> or value's length is bigger than
+ * three quarters of <code>MAX_KEY_LENGTH</code>
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void putByteArray (String key, byte[] value);
+
+ /**
+ * Add new preferences to this node using given key and string form of given
+ * value, or update value if preferences with given key has already existed.
+ * <p>
+ * The string form of given value is the result of invoking
+ * {@link Double#toString(double) Double.toString(double)}</p>
+ *
+ * @param key the preferences key to be added or be updated
+ * @param value the preferences value for the given key
+ * @throws NullPointerException
+ * if the given key is null
+ * @throws IllegalArgumentException
+ * if the given key's length is bigger than
+ * <code>MAX_KEY_LENGTH</code>
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void putDouble (String key, double value);
+
+ /**
+ * Add new preferences to this node using given key and string form of given
+ * value, or update value if preferences with given key has already existed.
+ * <p>
+ * The string form of given value is the result of invoking
+ * {@link Float#toString(float) Float.toString(float)}</p>
+ *
+ * @param key the preferences key to be added or be updated
+ * @param value the preferences value for the given key
+ * @throws NullPointerException
+ * if the given key is null
+ * @throws IllegalArgumentException
+ * if the given key's length is bigger than
+ * <code>MAX_KEY_LENGTH</code>
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void putFloat (String key, float value);
+
+ /**
+ * Add new preferences to this node using given key and string form of given
+ * value, or update value if preferences with given key has already existed.
+ * <p>
+ * The string form of given value is the result of invoking
+ * {@link Integer#toString(int) Integer.toString(int)}</p>
+ *
+ * @param key the preferences key to be added or be updated
+ * @param value the preferences value for the given key
+ * @throws NullPointerException
+ * if the given key is null
+ * @throws IllegalArgumentException
+ * if the given key's length is bigger than
+ * <code>MAX_KEY_LENGTH</code>
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void putInt (String key, int value);
+
+ /**
+ * Add new preferences to this node using given key and string form of given
+ * value, or update value if preferences with given key has already existed.
+ * <p>
+ * The string form of given value is the result of invoking
+ * {@link Long#toString(long) Long.toString(long)}</p>
+ *
+ * @param key the preferences key to be added or be updated
+ * @param value the preferences value for the given key
+ * @throws NullPointerException
+ * if the given key is null
+ * @throws IllegalArgumentException
+ * if the given key's length is bigger than
+ * <code>MAX_KEY_LENGTH</code>
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void putLong (String key, long value);
+
+ /**
+ * Remove the preferences mapped to the given key from this node.
+ *
+ * @param key the given preferences key to removed
+ * @throws NullPointerException
+ * if the given key is null
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void remove (String key);
+
+ /**
+ * Remove this preferences node and its all descendants. The removal maybe
+ * won't be persisted until the <code>flush()</code> method is invoked.
+ *
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ * @throws IllegalStateException
+ * if this node has been removed
+ * @throws UnsupportedOperationException
+ * if this is a root node
+ */
+ public abstract void removeNode() throws BackingStoreException;
+
+ /**
+ * Register an <code>NodeChangeListener</code> instance for this node, which
+ * will receive <code>NodeChangeEvent</code>. <code>NodeChangeEvent</code> will
+ * be produced when direct child node is added to or removed from this node.
+ *
+ * @param ncl the given listener to be registered
+ * @throws NullPointerException
+ * if the given listener is null
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void addNodeChangeListener (NodeChangeListener ncl);
+
+ /**
+ * Register an <code>PreferenceChangeListener</code> instance for this node, which
+ * will receive <code>PreferenceChangeEvent</code>. <code>PreferenceChangeEvent</code> will
+ * be produced when preference is added to, removed from or updated for this node.
+ *
+ * @param pcl the given listener to be registered
+ * @throws NullPointerException
+ * if the given listener is null
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void addPreferenceChangeListener (PreferenceChangeListener pcl);
+
+ /**
+ * Remove the given <code>NodeChangeListener</code> instance from this node.
+ *
+ * @param ncl the given listener to be removed
+ * @throws IllegalArgumentException
+ * if the given listener
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void removeNodeChangeListener (NodeChangeListener ncl);
+
+ /**
+ * Remove the given <code>PreferenceChangeListener</code> instance from this node.
+ *
+ * @param pcl the given listener to be removed
+ * @throws IllegalArgumentException
+ * if the given listener
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void removePreferenceChangeListener (PreferenceChangeListener pcl);
+
+ /**
+ * Synchronize this preferences node and its descendants' data with the back
+ * end preferences store. The changes of back end should be reflect by this
+ * node and its descendants, meanwhile, the changes of this node and descendants
+ * should be persisted.
+ *
+ * @throws BackingStoreException
+ * if backing store is unavailable or causes operation failure
+ * @throws IllegalStateException
+ * if this node has been removed
+ */
+ public abstract void sync() throws BackingStoreException;
+
+ /**
+ * Return the system preference node for the package of given class. The
+ * absolute path of the returned node is one slash followed by the given
+ * class's full package name with replacing each period ('.') with slash.
+ * For example, the preference's associated with class <code>Object<code>
+ * has absolute path like "/java/lang". As a special case, the unnamed
+ * package is associated with preference node "/<unnamed>".
+ *
+ * This method will create node and its ancestors if needed, and the new
+ * created nodes maybe won't be persisted until the <code>flush()</code>
+ * is invoked.
+ *
+ * @param c the given class
+ * @return the system preference node for the package of given class.
+ * @throws NullPointerException
+ * if the given class is null
+ * @throws SecurityException
+ * if <code>RuntimePermission("preferences")</code> is denied
+ * by a <code>SecurityManager</code>
+ */
+ public static Preferences systemNodeForPackage (Class<?> c) {
+ checkSecurity();
+ return factory.systemRoot().node(getNodeName(c));
+ }
+
+ /**
+ * Return the root node for system preference hierarchy.
+ *
+ * @return the root node for system preference hierarchy
+ * @throws SecurityException
+ * if <code>RuntimePermission("preferences")</code> is denied
+ * by a <code>SecurityManager</code>
+ */
+ public static Preferences systemRoot() {
+ checkSecurity();
+ return factory.systemRoot();
+ }
+
+ //check the RuntimePermission("preferences")
+ private static void checkSecurity() {
+ SecurityManager manager = System.getSecurityManager();
+ if(null != manager){
+ manager.checkPermission(PREFS_PERM);
+ }
+
+ }
+
+ /**
+ * Return the user preference node for the package of given class. The
+ * absolute path of the returned node is one slash followed by the given
+ * class's full package name with replacing each period ('.') with slash.
+ * For example, the preference's associated with class <code>Object<code>
+ * has absolute path like "/java/lang". As a special case, the unnamed
+ * package is associated with preference node "/<unnamed>".
+ *
+ * This method will create node and its ancestors if needed, and the new
+ * created nodes maybe won't be persisted until the <code>flush()</code>
+ * is invoked.
+ *
+ * @param c the given class
+ * @return the user preference node for the package of given class.
+ * @throws NullPointerException
+ * if the given class is null
+ * @throws SecurityException
+ * if <code>RuntimePermission("preferences")</code> is denied
+ * by a <code>SecurityManager</code>
+ */
+ public static Preferences userNodeForPackage (Class<?> c) {
+ checkSecurity();
+ return factory.userRoot().node(getNodeName(c));
+ }
+
+ //parse node's absolute path from class instance
+ private static String getNodeName(Class<?> c){
+ // ??? PREFS TODO change back to harmony code once getPackage
+ // delivers the correct results
+ // Package p = c.getPackage();
+ // if(null == p){
+ // return "/<unnamed>"; //$NON-NLS-1$
+ // }
+ // return "/"+p.getName().replace('.', '/'); //$NON-NLS-1$
+ int dotIndex = c.getName().lastIndexOf(".");
+ return "/" + c.getName().substring(0, dotIndex).replace(".", "/");
+ }
+
+ /**
+ * Return the root node for user preference hierarchy.
+ *
+ * @return the root node for user preference hierarchy
+ * @throws SecurityException
+ * if <code>RuntimePermission("preferences")</code> is denied
+ * by a <code>SecurityManager</code>
+ */
+ public static Preferences userRoot() {
+ checkSecurity();
+ return factory.userRoot();
+ }
+
+ /**
+ * Return a string description of this node. The format is "User/System
+ * Preference Node: " followed by this node's absolute path.
+ *
+ * @return a string description of this node
+ *
+ */
+ @Override
+ public abstract String toString();
+}
diff --git a/prefs/src/main/java/java/util/prefs/PreferencesFactory.java b/prefs/src/main/java/java/util/prefs/PreferencesFactory.java
new file mode 100644
index 0000000..e430c08
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/PreferencesFactory.java
@@ -0,0 +1,48 @@
+/* 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 java.util.prefs;
+
+/**
+ * This interface is used by {@link Preferences} class
+ * as factory class to create Preferences instance. This interface can be implemented
+ * and installed to replace the default preferences implementation.
+ *
+ * @see java.util.prefs.Preferences
+ *
+ * @since 1.4
+ */
+public interface PreferencesFactory {
+ /**
+ * Returns the root of the preferences hierarchy for the calling user
+ * context.
+ *
+ * @return The user root preferences node.
+ */
+ Preferences userRoot();
+
+ /**
+ * Returns the root of the system preferences hierarchy.
+ *
+ * @return The root of the system preferences hierarchy.
+ */
+ Preferences systemRoot();
+}
+
+
+
+
diff --git a/prefs/src/main/java/java/util/prefs/XMLParser.java b/prefs/src/main/java/java/util/prefs/XMLParser.java
new file mode 100644
index 0000000..a3fdb6c
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/XMLParser.java
@@ -0,0 +1,596 @@
+/* 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 java.util.prefs;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringReader;
+import java.nio.channels.FileChannel;
+import java.nio.channels.FileLock;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.apache.harmony.prefs.internal.nls.Messages;
+import org.w3c.dom.Document;
+import org.w3c.dom.DocumentType;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+
+/**
+ * Utility class for the Preferences import/export from XML file.
+ *
+ */
+class XMLParser {
+
+ /*
+ * Constant - the specified DTD URL
+ */
+ static final String PREFS_DTD_NAME = "http://java.sun.com/dtd/preferences.dtd"; //$NON-NLS-1$
+
+ /*
+ * Constant - the DTD string
+ */
+ static final String PREFS_DTD = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" //$NON-NLS-1$
+ + " <!ELEMENT preferences (root)>" //$NON-NLS-1$
+ + " <!ATTLIST preferences EXTERNAL_XML_VERSION CDATA \"0.0\" >" //$NON-NLS-1$
+ + " <!ELEMENT root (map, node*) >" //$NON-NLS-1$
+ + " <!ATTLIST root type (system|user) #REQUIRED >" //$NON-NLS-1$
+ + " <!ELEMENT node (map, node*) >" //$NON-NLS-1$
+ + " <!ATTLIST node name CDATA #REQUIRED >" //$NON-NLS-1$
+ + " <!ELEMENT map (entry*) >" //$NON-NLS-1$
+ + " <!ELEMENT entry EMPTY >" //$NON-NLS-1$
+ + " <!ATTLIST entry key CDATA #REQUIRED value CDATA #REQUIRED >"; //$NON-NLS-1$
+
+ /*
+ * Constant - the specified header
+ */
+ static final String HEADER = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"; //$NON-NLS-1$
+
+ /*
+ * Constant - the specified DOCTYPE
+ */
+ static final String DOCTYPE = "<!DOCTYPE preferences SYSTEM"; //$NON-NLS-1$
+
+ /*
+ * empty string array constant
+ */
+ private static final String[] EMPTY_SARRAY = new String[0];
+
+ /*
+ * Constant - used by FilePreferencesImpl, which is default implementation of Linux platform
+ */
+ private static final String FILE_PREFS = "<!DOCTYPE map SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>"; //$NON-NLS-1$
+
+ /*
+ * Constant - specify the DTD version
+ */
+ private static final float XML_VERSION = 1.0f;
+
+ /*
+ * DOM builder
+ */
+ private static final DocumentBuilder builder;
+
+ /*
+ * specify the indent level
+ */
+ private static int indent = -1;
+
+ /*
+ * init DOM builder
+ */
+ static {
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(false);
+ try {
+ builder = factory.newDocumentBuilder();
+ } catch (ParserConfigurationException e) {
+ throw new Error(e);
+ }
+ builder.setEntityResolver(new EntityResolver() {
+ public InputSource resolveEntity(String publicId, String systemId)
+ throws SAXException, IOException {
+ if (systemId.equals(PREFS_DTD_NAME)) {
+ InputSource result = new InputSource(new StringReader(
+ PREFS_DTD));
+ result.setSystemId(PREFS_DTD_NAME);
+ return result;
+ }
+ // prefs.1=Invalid DOCTYPE declaration: {0}
+ throw new SAXException(
+ Messages.getString("prefs.1", systemId)); //$NON-NLS-1$
+ }
+ });
+ builder.setErrorHandler(new ErrorHandler() {
+ public void warning(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public void error(SAXParseException e) throws SAXException {
+ throw e;
+ }
+
+ public void fatalError(SAXParseException e) throws SAXException {
+ throw e;
+ }
+ });
+ }
+
+ private XMLParser() {// empty constructor
+ }
+
+ /***************************************************************************
+ * utilities for Preferences export
+ **************************************************************************/
+ static void exportPrefs(Preferences prefs, OutputStream stream,
+ boolean withSubTree) throws IOException, BackingStoreException {
+ indent = -1;
+ // BEGIN android-modified
+ BufferedWriter out = new BufferedWriter(new OutputStreamWriter(stream, "UTF-8"), 8192); //$NON-NLS-1$
+ // END android-modified
+ out.write(HEADER);
+ out.newLine();
+ out.newLine();
+
+ out.write(DOCTYPE);
+ out.write(" '"); //$NON-NLS-1$
+ out.write(PREFS_DTD_NAME);
+ out.write("'>"); //$NON-NLS-1$
+ out.newLine();
+ out.newLine();
+
+ flushStartTag(
+ "preferences", new String[] { "EXTERNAL_XML_VERSION" }, new String[] { String.valueOf(XML_VERSION) }, out); //$NON-NLS-1$ //$NON-NLS-2$
+ flushStartTag(
+ "root", new String[] { "type" }, new String[] { prefs.isUserNode() ? "user" : "system" }, out); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
+ flushEmptyElement("map", out); //$NON-NLS-1$
+
+ StringTokenizer ancestors = new StringTokenizer(prefs.absolutePath(),
+ "/"); //$NON-NLS-1$
+ exportNode(ancestors, prefs, withSubTree, out);
+
+ flushEndTag("root", out); //$NON-NLS-1$
+ flushEndTag("preferences", out); //$NON-NLS-1$
+ out.flush();
+ out = null;
+ }
+
+ private static void exportNode(StringTokenizer ancestors,
+ Preferences prefs, boolean withSubTree, BufferedWriter out)
+ throws IOException, BackingStoreException {
+ if (ancestors.hasMoreTokens()) {
+ String name = ancestors.nextToken();
+ flushStartTag(
+ "node", new String[] { "name" }, new String[] { name }, out); //$NON-NLS-1$ //$NON-NLS-2$
+ if (ancestors.hasMoreTokens()) {
+ flushEmptyElement("map", out); //$NON-NLS-1$
+ exportNode(ancestors, prefs, withSubTree, out);
+ } else {
+ exportEntries(prefs, out);
+ if (withSubTree) {
+ exportSubTree(prefs, out);
+ }
+ }
+ flushEndTag("node", out); //$NON-NLS-1$
+ }
+ }
+
+ private static void exportSubTree(Preferences prefs, BufferedWriter out)
+ throws BackingStoreException, IOException {
+ String[] names = prefs.childrenNames();
+ if (names.length > 0) {
+ for (int i = 0; i < names.length; i++) {
+ Preferences child = prefs.node(names[i]);
+ flushStartTag(
+ "node", new String[] { "name" }, new String[] { names[i] }, out); //$NON-NLS-1$ //$NON-NLS-2$
+ exportEntries(child, out);
+ exportSubTree(child, out);
+ flushEndTag("node", out); //$NON-NLS-1$
+ }
+ }
+ }
+
+ private static void exportEntries(Preferences prefs, BufferedWriter out)
+ throws BackingStoreException, IOException {
+ String[] keys = prefs.keys();
+ String[] values = new String[keys.length];
+ for (int i = 0; i < keys.length; i++) {
+ values[i] = prefs.get(keys[i], null);
+ }
+ exportEntries(keys, values, out);
+ }
+
+ private static void exportEntries(String[] keys, String[] values,
+ BufferedWriter out) throws IOException {
+ if (keys.length == 0) {
+ flushEmptyElement("map", out); //$NON-NLS-1$
+ return;
+ }
+ flushStartTag("map", out); //$NON-NLS-1$
+ for (int i = 0; i < keys.length; i++) {
+ if (values[i] != null) {
+ flushEmptyElement(
+ "entry", new String[] { "key", "value" }, new String[] { keys[i], values[i] }, out); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ }
+ flushEndTag("map", out); //$NON-NLS-1$
+ }
+
+ private static void flushEndTag(String tagName, BufferedWriter out)
+ throws IOException {
+ flushIndent(indent--, out);
+ out.write("</"); //$NON-NLS-1$
+ out.write(tagName);
+ out.write(">"); //$NON-NLS-1$
+ out.newLine();
+ }
+
+ private static void flushEmptyElement(String tagName, BufferedWriter out)
+ throws IOException {
+ flushIndent(++indent, out);
+ out.write("<"); //$NON-NLS-1$
+ out.write(tagName);
+ out.write(" />"); //$NON-NLS-1$
+ out.newLine();
+ indent--;
+ }
+
+ private static void flushEmptyElement(String tagName, String[] attrKeys,
+ String[] attrValues, BufferedWriter out) throws IOException {
+ flushIndent(++indent, out);
+ out.write("<"); //$NON-NLS-1$
+ out.write(tagName);
+ flushPairs(attrKeys, attrValues, out);
+ out.write(" />"); //$NON-NLS-1$
+ out.newLine();
+ indent--;
+ }
+
+ private static void flushPairs(String[] attrKeys, String[] attrValues,
+ BufferedWriter out) throws IOException {
+ for (int i = 0; i < attrKeys.length; i++) {
+ out.write(" "); //$NON-NLS-1$
+ out.write(attrKeys[i]);
+ out.write("=\""); //$NON-NLS-1$
+ out.write(htmlEncode(attrValues[i]));
+ out.write("\""); //$NON-NLS-1$
+ }
+ }
+
+ private static void flushIndent(int ind, BufferedWriter out)
+ throws IOException {
+ for (int i = 0; i < ind; i++) {
+ out.write(" "); //$NON-NLS-1$
+ }
+ }
+
+ private static void flushStartTag(String tagName, String[] attrKeys,
+ String[] attrValues, BufferedWriter out) throws IOException {
+ flushIndent(++indent, out);
+ out.write("<"); //$NON-NLS-1$
+ out.write(tagName);
+ flushPairs(attrKeys, attrValues, out);
+ out.write(">"); //$NON-NLS-1$
+ out.newLine();
+ }
+
+ private static void flushStartTag(String tagName, BufferedWriter out)
+ throws IOException {
+ flushIndent(++indent, out);
+ out.write("<"); //$NON-NLS-1$
+ out.write(tagName);
+ out.write(">"); //$NON-NLS-1$
+ out.newLine();
+ }
+
+ private static String htmlEncode(String s) {
+ StringBuffer sb = new StringBuffer();
+ char c;
+ for (int i = 0; i < s.length(); i++) {
+ c = s.charAt(i);
+ switch (c) {
+ case '<':
+ sb.append("&lt;"); //$NON-NLS-1$
+ break;
+ case '>':
+ sb.append("&gt;"); //$NON-NLS-1$
+ break;
+ case '&':
+ sb.append("&amp;"); //$NON-NLS-1$
+ break;
+ case '\\':
+ sb.append("&apos;"); //$NON-NLS-1$
+ break;
+ case '"':
+ sb.append("&quot;"); //$NON-NLS-1$
+ break;
+ default:
+ sb.append(c);
+ }
+ }
+ return sb.toString();
+ }
+
+ /***************************************************************************
+ * utilities for Preferences import
+ **************************************************************************/
+ static void importPrefs(InputStream in) throws IOException,
+ InvalidPreferencesFormatException {
+ try {
+ // load XML document
+ Document doc = builder.parse(new InputSource(in));
+
+ // check preferences' export version
+ Element preferences;
+ preferences = doc.getDocumentElement();
+ String version = preferences.getAttribute("EXTERNAL_XML_VERSION"); //$NON-NLS-1$
+ if (version != null && Float.parseFloat(version) > XML_VERSION) {
+ // prefs.2=This preferences exported version is not supported:{0}
+ throw new InvalidPreferencesFormatException(
+ Messages.getString("prefs.2", version)); //$NON-NLS-1$
+ }
+
+ // check preferences root's type
+ Element root = (Element) preferences
+ .getElementsByTagName("root").item(0); //$NON-NLS-1$
+ Preferences prefsRoot = null;
+ String type = root.getAttribute("type"); //$NON-NLS-1$
+ if (type.equals("user")) { //$NON-NLS-1$
+ prefsRoot = Preferences.userRoot();
+ } else {
+ prefsRoot = Preferences.systemRoot();
+ }
+
+ // load node
+ loadNode(prefsRoot, root);
+ } catch (FactoryConfigurationError e) {
+ throw new InvalidPreferencesFormatException(e);
+ } catch (SAXException e) {
+ throw new InvalidPreferencesFormatException(e);
+ }
+ }
+
+ private static void loadNode(Preferences prefs, Element node) {
+ // load preferences
+ NodeList children = selectNodeList(node, "node"); //$NON-NLS-1$
+ NodeList entries = selectNodeList(node, "map/entry"); //$NON-NLS-1$
+ int childNumber = children.getLength();
+ Preferences[] prefChildren = new Preferences[childNumber];
+ int entryNumber = entries.getLength();
+ synchronized (((AbstractPreferences) prefs).lock) {
+ if (((AbstractPreferences) prefs).isRemoved()) {
+ return;
+ }
+ for (int i = 0; i < entryNumber; i++) {
+ Element entry = (Element) entries.item(i);
+ String key = entry.getAttribute("key"); //$NON-NLS-1$
+ String value = entry.getAttribute("value"); //$NON-NLS-1$
+ prefs.put(key, value);
+ }
+ // get children preferences node
+ for (int i = 0; i < childNumber; i++) {
+ Element child = (Element) children.item(i);
+ String name = child.getAttribute("name"); //$NON-NLS-1$
+ prefChildren[i] = prefs.node(name);
+ }
+ }
+
+ // load children nodes after unlock
+ for (int i = 0; i < childNumber; i++) {
+ loadNode(prefChildren[i], (Element) children.item(i));
+ }
+ }
+
+
+ // ??? PREFS TODO dirty implementation of a method from javax.xml.xpath
+ // should be replaced with a call to a good impl of this method
+ private static NodeList selectNodeList(Element documentElement, String string) {
+
+ NodeList result = null;
+
+ ArrayList<Node> input = new ArrayList<Node>();
+
+ String[] path = string.split("/");
+
+ NodeList childNodes = documentElement.getChildNodes();
+
+ if(path[0].equals("entry") || path[0].equals("node")) {
+ for(int i = 0; i < childNodes.getLength(); i++) {
+ Object next = childNodes.item(i);
+ if(next instanceof Element) {
+ if(((Element) next).getNodeName().equals(path[0])
+ && next instanceof Node) {
+ input.add((Node)next);
+ }
+ }
+ }
+ } else if(path[0].equals("map") && path[1].equals("entry")) {
+ for(int i = 0; i < childNodes.getLength(); i++) {
+ Object next = childNodes.item(i);
+ if(next instanceof Element) {
+ if(((Element) next).getNodeName().equals(path[0])
+ && next instanceof Node) {
+ NodeList nextChildNodes = ((Node)next).getChildNodes();
+ for(int j = 0; j < nextChildNodes.getLength(); j++) {
+ Object subnext = nextChildNodes.item(j);
+ if(subnext instanceof Element) {
+ if(((Element)subnext).getNodeName().equals(path[1])) {
+ input.add((Node)subnext);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ result = new NodeSet(input.iterator());
+
+ return result;
+ }
+
+ /***************************************************************************
+ * utilities for FilePreferencesImpl, which is default implementation of Linux platform
+ **************************************************************************/
+ /**
+ * load preferences from file, if cannot load, create a new one FIXME: need
+ * lock or not?
+ *
+ * @param file the XML file to be read
+ * @return Properties instance which indicates the preferences key-value pairs
+ */
+ static Properties loadFilePrefs(final File file) {
+ return AccessController.doPrivileged(new PrivilegedAction<Properties>() {
+ public Properties run() {
+ return loadFilePrefsImpl(file);
+ }
+ });
+
+ // try {
+ // //FIXME: lines below can be deleted, because it is not required to
+ // persistent at the very beginning
+ // flushFilePrefs(file, result);
+ // } catch (IOException e) {
+ // e.printStackTrace();
+ // }
+ }
+
+ static Properties loadFilePrefsImpl(final File file) {
+ Properties result = new Properties();
+ if (!file.exists()) {
+ file.getParentFile().mkdirs();
+ } else if (file.canRead()) {
+ InputStream in = null;
+ FileLock lock = null;
+ try {
+
+ FileInputStream istream = new FileInputStream(file);
+ // BEGIN android-modified
+ in = new BufferedInputStream(istream, 8192);
+ // END android-modified
+ FileChannel channel = istream.getChannel();
+ lock = channel.lock(0L, Long.MAX_VALUE, true);
+ Document doc = builder.parse(in);
+ NodeList entries = selectNodeList(doc
+ .getDocumentElement(), "entry"); //$NON-NLS-1$
+ int length = entries.getLength();
+ for (int i = 0; i < length; i++) {
+ Element node = (Element) entries.item(i);
+ String key = node.getAttribute("key"); //$NON-NLS-1$
+ String value = node.getAttribute("value"); //$NON-NLS-1$
+ result.setProperty(key, value);
+ }
+ return result;
+ } catch (Exception e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ lock.release();
+ } catch (Exception e) {//ignore
+ }
+ try {
+ in.close();
+ } catch (Exception e) {//ignore
+ }
+ }
+ } else {
+ file.delete();
+ }
+ return result;
+ }
+
+ /**
+ *
+ * @param file
+ * @param prefs
+ * @throws PrivilegedActionException
+ */
+ static void flushFilePrefs(final File file, final Properties prefs) throws PrivilegedActionException {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
+ public Object run() throws IOException {
+ flushFilePrefsImpl(file, prefs);
+ return null;
+ }
+ });
+ }
+
+ static void flushFilePrefsImpl(File file, Properties prefs) throws IOException {
+ BufferedWriter out = null;
+ FileLock lock = null;
+ try {
+ FileOutputStream ostream = new FileOutputStream(file);
+ // BEGIN android-modified
+ out = new BufferedWriter(new OutputStreamWriter(ostream, "UTF-8"), 8192); //$NON-NLS-1$
+ // END android-modified
+ FileChannel channel = ostream.getChannel();
+ lock = channel.lock();
+ out.write(HEADER);
+ out.newLine();
+ out.write(FILE_PREFS);
+ out.newLine();
+ if (prefs.size() == 0) {
+ exportEntries(EMPTY_SARRAY, EMPTY_SARRAY, out);
+ } else {
+ String[] keys = prefs.keySet().toArray(new String[prefs.size()]);
+ int length = keys.length;
+ String[] values = new String[length];
+ for (int i = 0; i < length; i++) {
+ values[i] = prefs.getProperty(keys[i]);
+ }
+ exportEntries(keys, values, out);
+ }
+ out.flush();
+ } finally {
+ try {
+ lock.release();
+ } catch (Exception e) {//ignore
+ }
+ try {
+ if (null != out) {
+ out.close();
+ }
+ } catch (Exception e) {//ignore
+ }
+ }
+ }
+}
+
+
diff --git a/prefs/src/main/java/java/util/prefs/package.html b/prefs/src/main/java/java/util/prefs/package.html
new file mode 100644
index 0000000..bb68c34
--- /dev/null
+++ b/prefs/src/main/java/java/util/prefs/package.html
@@ -0,0 +1,11 @@
+<html>
+ <body>
+ <p>
+ Provides a preferences mechanism, that is, a means of writing
+ configuration data (key/value pairs) to a persistent data store and
+ retrieving it from there. There are two different kinds of stores
+ available, one for storing user data and one for storing system
+ configuration data.
+ </p>
+ </body>
+</html>
diff --git a/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java b/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java
new file mode 100644
index 0000000..18b0b3a
--- /dev/null
+++ b/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+/*
+ * THE FILE HAS BEEN AUTOGENERATED BY MSGTOOL TOOL.
+ * All changes made to this file manually will be overwritten
+ * if this tool runs again. Better make changes in the template file.
+ */
+
+package org.apache.harmony.prefs.internal.nls;
+
+// BEGIN android-added
+import org.apache.harmony.luni.util.MsgHelp;
+// END android-added
+
+/**
+ * This class retrieves strings from a resource bundle and returns them,
+ * formatting them with MessageFormat when required.
+ * <p>
+ * It is used by the system classes to provide national language support, by
+ * looking up messages in the <code>
+ * org.apache.harmony.prefs.internal.nls.messages
+ * </code>
+ * resource bundle. Note that if this file is not available, or an invalid key
+ * is looked up, or resource bundle support is not available, the key itself
+ * will be returned as the associated message. This means that the <em>KEY</em>
+ * should a reasonable human-readable (english) string.
+ *
+ */
+public class Messages {
+
+ // BEGIN android-changed
+ private static final String sResource =
+ "org.apache.harmony.prefs.internal.nls.messages";
+ // END android-changed
+
+ /**
+ * Retrieves a message which has no arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg) {
+ // BEGIN android-changed
+ return MsgHelp.getString(sResource, msg);
+ // END android-changed
+ }
+
+ /**
+ * Retrieves a message which takes 1 argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * Object the object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg) {
+ return getString(msg, new Object[] { arg });
+ }
+
+ /**
+ * Retrieves a message which takes 1 integer argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * int the integer to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, int arg) {
+ return getString(msg, new Object[] { Integer.toString(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 1 character argument.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg
+ * char the character to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, char arg) {
+ return getString(msg, new Object[] { String.valueOf(arg) });
+ }
+
+ /**
+ * Retrieves a message which takes 2 arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param arg1
+ * Object an object to insert in the formatted output.
+ * @param arg2
+ * Object another object to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object arg1, Object arg2) {
+ return getString(msg, new Object[] { arg1, arg2 });
+ }
+
+ /**
+ * Retrieves a message which takes several arguments.
+ *
+ * @param msg
+ * String the key to look up.
+ * @param args
+ * Object[] the objects to insert in the formatted output.
+ * @return String the message for that key in the system message bundle.
+ */
+ static public String getString(String msg, Object[] args) {
+ // BEGIN android-changed
+ return MsgHelp.getString(sResource, msg, args);
+ // END android-changed
+ }
+}
diff --git a/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/messages.properties b/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/messages.properties
new file mode 100644
index 0000000..8940685
--- /dev/null
+++ b/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/messages.properties
@@ -0,0 +1,33 @@
+# 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.
+
+# messages for EN locale
+prefs.0=Inputstream cannot be null\!
+prefs.1=Invalid DOCTYPE declaration: {0}
+prefs.10=Cannot initiate PreferencesFactory: {0}. Caused by {1}
+prefs.2=This preferences exported version is not supported:{0}
+prefs.3=Cannot get children names for {0}!
+prefs.4=Cannot remove {0}!
+prefs.5=Stream is null
+prefs.6=Name cannot end with '/'\!
+prefs.7=Name cannot contains consecutive '/'\!
+prefs.8=Name length is too long: {0}
+prefs.9=This node has been removed\!
+prefs.A=Cannot remove root node\!
+prefs.B=Enumerate child nodes error\!
+prefs.C=Flush error\!
+prefs.D=Enumerate keys error\!
+prefs.E=Access denied\!
+prefs.F=Remove node error\!
diff --git a/prefs/src/main/resources/META-INF/services/java.util.prefs.PreferencesFactory b/prefs/src/main/resources/META-INF/services/java.util.prefs.PreferencesFactory
new file mode 100644
index 0000000..ebb514c
--- /dev/null
+++ b/prefs/src/main/resources/META-INF/services/java.util.prefs.PreferencesFactory
@@ -0,0 +1 @@
+java.util.prefs.FilePreferencesFactoryImpl
diff --git a/prefs/src/test/java/java/util/prefs/FilePreferencesFactoryTestImpl.java b/prefs/src/test/java/java/util/prefs/FilePreferencesFactoryTestImpl.java
new file mode 100644
index 0000000..bb6f2dd
--- /dev/null
+++ b/prefs/src/test/java/java/util/prefs/FilePreferencesFactoryTestImpl.java
@@ -0,0 +1,44 @@
+/* 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 java.util.prefs;
+
+/**
+ * Default implementation of <code>PreferencesFactory</code> for Linux
+ * platform, using file system as back end.
+ *
+ * @since 1.4
+ */
+class FilePreferencesFactoryTestImpl implements PreferencesFactory {
+ // user root preferences
+ private static final Preferences USER_ROOT = new FilePreferencesTestImpl(true);
+
+ // system root preferences
+ private static final Preferences SYSTEM_ROOT = new FilePreferencesTestImpl(false);
+
+ public FilePreferencesFactoryTestImpl() {
+ super();
+ }
+
+ public Preferences userRoot() {
+ return USER_ROOT;
+ }
+
+ public Preferences systemRoot() {
+ return SYSTEM_ROOT;
+ }
+
+}
diff --git a/prefs/src/test/java/java/util/prefs/FilePreferencesTestImpl.java b/prefs/src/test/java/java/util/prefs/FilePreferencesTestImpl.java
new file mode 100644
index 0000000..b31563b
--- /dev/null
+++ b/prefs/src/test/java/java/util/prefs/FilePreferencesTestImpl.java
@@ -0,0 +1,236 @@
+/* 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 java.util.prefs;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+
+import org.apache.harmony.prefs.internal.nls.Messages;
+
+/**
+ * Default implementation of <code>AbstractPreferences</code> for Linux platform,
+ * using file system as back end.
+ *
+ * TODO some sync mechanism with backend, Performance - check file edit date
+ *
+ * @since 1.4
+ */
+class FilePreferencesTestImpl extends AbstractPreferences {
+
+ /*
+ * --------------------------------------------------------------
+ * Class fields
+ * --------------------------------------------------------------
+ */
+
+ //prefs file name
+ private static final String PREFS_FILE_NAME = "prefs.xml"; //$NON-NLS-1$
+
+ //home directory for user prefs
+ private static String USER_HOME;
+
+ //home directory for system prefs
+ private static String SYSTEM_HOME;
+
+ /*
+ * --------------------------------------------------------------
+ * Class initializer
+ * --------------------------------------------------------------
+ */
+ static {
+ AccessController.doPrivileged(new PrivilegedAction<Void>() {
+ public Void run() {
+ USER_HOME = System.getProperty("java.io.tmpdir") + "/.userPrefsTest";//$NON-NLS-1$ //$NON-NLS-2$
+ SYSTEM_HOME = System.getProperty("java.io.tmpdir") + "/.systemPrefsTest";//$NON-NLS-1$//$NON-NLS-2$
+ return null;
+ }
+
+ });
+ }
+
+ /*
+ * --------------------------------------------------------------
+ * Instance fields
+ * --------------------------------------------------------------
+ */
+
+ //file path for this preferences node
+ private String path;
+
+ //internal cache for prefs key-value pair
+ private Properties prefs;
+
+ //file represents this preferences node
+ private File prefsFile;
+
+ //parent dir for this preferences node
+ private File dir;
+
+ //cache for removed prefs key-value pair
+ private Set<String> removed = new HashSet<String>();
+
+ //cache for updated prefs key-value pair
+ private Set<String> updated = new HashSet<String>();
+
+ /*
+ * --------------------------------------------------------------
+ * Constructors
+ * --------------------------------------------------------------
+ */
+
+ /**
+ * Construct root <code>FilePreferencesTestImpl</code> instance, construct
+ * user root if userNode is true, system root otherwise
+ */
+ FilePreferencesTestImpl(boolean userNode) {
+ super(null, ""); //$NON-NLS-1$
+ this.userNode = userNode;
+ path = userNode ? USER_HOME : SYSTEM_HOME;
+ initPrefs();
+ }
+
+ /**
+ * Construct a prefs using given parent and given name
+ */
+ private FilePreferencesTestImpl(AbstractPreferences parent, String name) {
+ super(parent, name);
+ path = ((FilePreferencesTestImpl) parent).path + File.separator + name;
+ initPrefs();
+ }
+
+ private void initPrefs() {
+ dir = new File(path);
+ newNode = (AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ return Boolean.valueOf(!dir.exists());
+ }
+ })).booleanValue();
+ prefsFile = new File(path + File.separator + PREFS_FILE_NAME);
+ prefs = XMLParser.loadFilePrefs(prefsFile);
+ }
+
+ @Override
+ protected String[] childrenNamesSpi() throws BackingStoreException {
+ String[] names = AccessController
+ .doPrivileged(new PrivilegedAction<String[]>() {
+ public String[] run() {
+ return dir.list(new FilenameFilter() {
+ public boolean accept(File parent, String name) {
+ return new File(path + File.separator + name).isDirectory();
+ }
+ });
+
+ }
+ });
+ if (null == names) {// file is not a directory, exception case
+ // prefs.3=Cannot get children names for {0}!
+ throw new BackingStoreException(
+ Messages.getString("prefs.3", toString())); //$NON-NLS-1$
+ }
+ return names;
+ }
+
+ @Override
+ protected AbstractPreferences childSpi(String name) {
+ FilePreferencesTestImpl child = new FilePreferencesTestImpl(this, name);
+ return child;
+ }
+
+ @Override
+ protected void flushSpi() throws BackingStoreException {
+ try {
+ //if removed, return
+ if(isRemoved()){
+ return;
+ }
+ // reload
+ Properties currentPrefs = XMLParser.loadFilePrefs(prefsFile);
+ // merge
+ Iterator<String> it = removed.iterator();
+ while (it.hasNext()) {
+ currentPrefs.remove(it.next());
+ }
+ removed.clear();
+ it = updated.iterator();
+ while (it.hasNext()) {
+ Object key = it.next();
+ currentPrefs.put(key, prefs.get(key));
+ }
+ updated.clear();
+ // flush
+ prefs = currentPrefs;
+ XMLParser.flushFilePrefs(prefsFile, prefs);
+ } catch (Exception e) {
+ throw new BackingStoreException(e);
+ }
+ }
+
+ @Override
+ protected String getSpi(String key) {
+ try {
+ if (null == prefs) {
+ prefs = XMLParser.loadFilePrefs(prefsFile);
+ }
+ return prefs.getProperty(key);
+ } catch (Exception e) {// if Exception happened, return null
+ return null;
+ }
+ }
+
+ @Override
+ protected String[] keysSpi() throws BackingStoreException {
+ return prefs.keySet().toArray(new String[0]);
+ }
+
+ @Override
+ protected void putSpi(String name, String value) {
+ prefs.setProperty(name, value);
+ updated.add(name);
+ }
+
+ @Override
+ protected void removeNodeSpi() throws BackingStoreException {
+ boolean removeSucceed = (AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
+ public Boolean run() {
+ prefsFile.delete();
+ return Boolean.valueOf(dir.delete());
+ }
+ })).booleanValue();
+ if (!removeSucceed) {
+ // prefs.4=Cannot remove {0}!
+ throw new BackingStoreException(Messages.getString("prefs.4", toString())); //$NON-NLS-1$
+ }
+ }
+
+ @Override
+ protected void removeSpi(String key) {
+ prefs.remove(key);
+ updated.remove(key);
+ removed.add(key);
+ }
+
+ @Override
+ protected void syncSpi() throws BackingStoreException {
+ flushSpi();
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java
new file mode 100644
index 0000000..8f19666
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java
@@ -0,0 +1,1881 @@
+/*
+ * 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.harmony.prefs.tests.java.util.prefs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Arrays;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.NodeChangeEvent;
+import java.util.prefs.NodeChangeListener;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import junit.framework.TestCase;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * TODO: refine this test to adapt all implementations
+ *
+ */
+public class AbstractPreferencesTest extends TestCase {
+
+ AbstractPreferences pref;
+
+ static AbstractPreferences root = null;
+
+ static AbstractPreferences parent = null;
+
+ final static String longKey;
+
+ final static String longValue;
+
+ final static String longName;
+
+ MockNodeChangeListener nl;
+
+ MockPreferenceChangeListener pl;
+
+ static {
+ StringBuffer key = new StringBuffer(Preferences.MAX_KEY_LENGTH);
+ for (int i = 0; i < Preferences.MAX_KEY_LENGTH; i++) {
+ key.append('a');
+ }
+ longKey = key.toString();
+ StringBuffer value = new StringBuffer(Preferences.MAX_VALUE_LENGTH);
+ for (int i = 0; i < Preferences.MAX_VALUE_LENGTH; i++) {
+ value.append('a');
+ }
+ longValue = value.toString();
+
+ StringBuffer name = new StringBuffer(Preferences.MAX_NAME_LENGTH);
+ for (int i = 0; i < Preferences.MAX_NAME_LENGTH; i++) {
+ name.append('a');
+ }
+ longName = name.toString();
+ }
+
+ protected void setUp() throws Exception {
+ super.setUp();
+ root = (AbstractPreferences) Preferences.userRoot();
+ parent = (AbstractPreferences) Preferences
+ .userNodeForPackage(Preferences.class);
+ // FIXME: change here is dangerous
+ // pref = new MockAbstractPreferences((AbstractPreferences) parent,
+ // "mock");
+
+ pref = (AbstractPreferences) parent.node("mock");
+ }
+
+ protected void tearDown() throws Exception {
+ try {
+ if (pref instanceof MockAbstractPreferences) {
+ ((MockAbstractPreferences) pref)
+ .setResult(MockAbstractPreferences.NORMAL);
+ }
+ pref.removeNode();
+ } catch (Exception e) {
+ }
+ super.tearDown();
+ }
+
+ public void testConstructor() throws BackingStoreException {
+ try {
+ pref = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mo/ck");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref = new MockAbstractPreferences(null, "mock");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ new MockAbstractPreferences(null, " ");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ new MockAbstractPreferences(pref, "");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ new MockAbstractPreferences(pref, null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ new MockAbstractPreferences(pref, " ");
+
+ Preferences p2 = new MockAbstractPreferences(null, "");
+ assertNotSame(p2, Preferences.systemRoot());
+ assertNotSame(p2, Preferences.userRoot());
+ assertFalse(p2.isUserNode());
+
+ p2 = new MockAbstractPreferences((AbstractPreferences) Preferences
+ .userRoot(), "mock");
+ assertNotSame(p2, pref);
+ p2.removeNode();
+ }
+
+ public void testProtectedFields() throws BackingStoreException {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = new MockAbstractPreferences(pref, "newNode");
+ assertFalse(p.getNewNode());
+ assertSame(p.getLock().getClass(), Object.class);
+
+ p = (MockAbstractPreferences) pref.node("child");
+ assertTrue(p.getNewNode());
+
+ p = (MockAbstractPreferences) ((MockAbstractPreferences) pref)
+ .publicChildSpi("child2");
+ assertTrue(p.getNewNode());
+ }
+
+ public void testToString() {
+ assertEquals("User Preference Node: " + pref.absolutePath(), pref
+ .toString());
+
+ pref = new MockAbstractPreferences((AbstractPreferences) Preferences
+ .systemRoot(), "mock");
+ assertEquals("System Preference Node: " + pref.absolutePath(), pref
+ .toString());
+ }
+
+ public void testAbsolutePath() {
+ assertEquals("/java/util/prefs/mock", pref.absolutePath());
+
+ pref = new MockAbstractPreferences(pref, " ");
+ assertEquals("/java/util/prefs/mock/ ", pref.absolutePath());
+ }
+
+ public void testChildrenNames() throws BackingStoreException {
+ assertEquals(0, pref.childrenNames().length);
+
+ Preferences child1 = pref.node("child1");
+
+ pref.node("child2");
+ pref.node("child3");
+ child1.node("subchild1");
+
+ assertSame(pref, child1.parent());
+ assertEquals(3, pref.childrenNames().length);
+ assertEquals("child1", pref.childrenNames()[0]);
+ assertEquals(1, child1.childrenNames().length);
+ assertEquals("subchild1", child1.childrenNames()[0]);
+ }
+
+ public void testClear() throws BackingStoreException {
+ pref.put("testClearKey", "testClearValue");
+ pref.put("testClearKey1", "testClearValue1");
+ assertEquals("testClearValue", pref.get("testClearKey", null));
+ assertEquals("testClearValue1", pref.get("testClearKey1", null));
+ pref.clear();
+ assertNull(pref.get("testClearKey", null));
+ assertNull(pref.get("testClearKey1", null));
+ }
+
+ public void testGet() throws BackingStoreException {
+ assertNull(pref.get("", null));
+ assertEquals("default", pref.get("key", "default"));
+ assertNull(pref.get("key", null));
+ pref.put("testGetkey", "value");
+ assertNull(pref.get("testGetKey", null));
+ assertEquals("value", pref.get("testGetkey", null));
+
+ try {
+ pref.get(null, "abc");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.get("", "abc");
+ pref.get("key", null);
+ pref.get("key", "");
+ pref.putFloat("floatKey", 1.0f);
+ assertEquals("1.0", pref.get("floatKey", null));
+
+ pref.removeNode();
+ try {
+ pref.get("key", "abc");
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.get(null, "abc");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ }
+
+ public void testGetBoolean() {
+ try {
+ pref.getBoolean(null, false);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetBooleanKey", "false");
+ pref.put("testGetBooleanKey2", "value");
+ assertFalse(pref.getBoolean("testGetBooleanKey", true));
+ assertTrue(pref.getBoolean("testGetBooleanKey2", true));
+ }
+
+ public void testPutByteArray() {
+ try {
+ pref.putByteArray(null, new byte[0]);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.putByteArray("testPutByteArrayKey4", null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.putByteArray(longKey, new byte[0]);
+ try {
+ pref.putByteArray(longKey + "a", new byte[0]);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ byte[] longArray = new byte[(int) (Preferences.MAX_VALUE_LENGTH * 0.74)];
+ byte[] longerArray = new byte[(int) (Preferences.MAX_VALUE_LENGTH * 0.75) + 1];
+ pref.putByteArray(longKey, longArray);
+ try {
+ pref.putByteArray(longKey, longerArray);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ pref.putByteArray("testPutByteArrayKey", new byte[0]);
+ assertEquals("", pref.get("testPutByteArrayKey", null));
+ assertTrue(Arrays.equals(new byte[0], pref.getByteArray(
+ "testPutByteArrayKey", null)));
+
+ pref.putByteArray("testPutByteArrayKey3", new byte[] { 'a', 'b', 'c' });
+ assertEquals("YWJj", pref.get("testPutByteArrayKey3", null));
+ assertTrue(Arrays.equals(new byte[] { 'a', 'b', 'c' }, pref
+ .getByteArray("testPutByteArrayKey3", null)));
+ }
+
+ public void testGetByteArray() throws UnsupportedEncodingException {
+ try {
+ pref.getByteArray(null, new byte[0]);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ byte[] b64Array = new byte[] { 0x59, 0x57, 0x4a, 0x6a };// BASE64
+ // encoding for
+ // "abc"
+
+ pref.put("testGetByteArrayKey", "abc=");
+ pref.put("testGetByteArrayKey2", new String(b64Array));
+ pref.put("invalidKey", "<>?");
+ // assertTrue(Arrays.equals(new byte[0], p.getByteArray(
+ // "testGetByteArrayKey", new byte[0])));
+ assertTrue(Arrays.equals(new byte[] { 105, -73 }, pref.getByteArray(
+ "testGetByteArrayKey", new byte[0])));
+ assertTrue(Arrays.equals(new byte[] { 'a', 'b', 'c' }, pref
+ .getByteArray("testGetByteArrayKey2", new byte[0])));
+ assertTrue(Arrays.equals(new byte[0], pref.getByteArray("invalidKey",
+ new byte[0])));
+
+ pref.putByteArray("testGetByteArrayKey3", b64Array);
+ pref.putByteArray("testGetByteArrayKey4", "abc".getBytes());
+ assertTrue(Arrays.equals(b64Array, pref.getByteArray(
+ "testGetByteArrayKey3", new byte[0])));
+ assertTrue(Arrays.equals("abc".getBytes(), pref.getByteArray(
+ "testGetByteArrayKey4", new byte[0])));
+ }
+
+ public void testGetDouble() {
+ try {
+ pref.getDouble(null, 0);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetDoubleKey", "1");
+ pref.put("testGetDoubleKey2", "value");
+ pref.putDouble("testGetDoubleKey3", 1);
+ pref.putInt("testGetDoubleKey4", 1);
+ assertEquals(1.0, pref.getDouble("testGetDoubleKey", 0.0), 0);
+ assertEquals(0.0, pref.getDouble("testGetDoubleKey2", 0.0), 0);
+ assertEquals(1.0, pref.getDouble("testGetDoubleKey3", 0.0), 0);
+ assertEquals(1.0, pref.getDouble("testGetDoubleKey4", 0.0), 0);
+ }
+
+ public void testGetFloat() {
+ try {
+ pref.getFloat(null, 0f);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.put("testGetFloatKey", "1");
+ pref.put("testGetFloatKey2", "value");
+ assertEquals(1f, pref.getFloat("testGetFloatKey", 0f), 0); //$NON-NLS-1$
+ assertEquals(0f, pref.getFloat("testGetFloatKey2", 0f), 0);
+ }
+
+ public void testGetInt() {
+ try {
+ pref.getInt(null, 0);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetIntKey", "1");
+ pref.put("testGetIntKey2", "value");
+ assertEquals(1, pref.getInt("testGetIntKey", 0));
+ assertEquals(0, pref.getInt("testGetIntKey2", 0));
+ }
+
+ public void testGetLong() {
+ try {
+ pref.getLong(null, 0);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetLongKey", "1");
+ pref.put("testGetLongKey2", "value");
+ assertEquals(1, pref.getInt("testGetLongKey", 0));
+ assertEquals(0, pref.getInt("testGetLongKey2", 0));
+ }
+
+ public void testIsUserNode() {
+ assertTrue(pref.isUserNode());
+
+ pref = new MockAbstractPreferences((AbstractPreferences) Preferences
+ .systemRoot(), "mock");
+ assertFalse(pref.isUserNode());
+ }
+
+ // TODO, how to test the "stored defaults"
+ // TODO, how to test the multi-thread
+ public void testKeys() throws BackingStoreException {
+ assertEquals(0, pref.keys().length);
+
+ pref.put("key0", "value");
+ pref.put("key1", "value1");
+ pref.put("key2", "value2");
+ pref.put("key3", "value3");
+
+ String[] keys = pref.keys();
+ assertEquals(4, keys.length);
+ for (int i = 0; i < keys.length; i++) {
+ assertEquals(0, keys[i].indexOf("key"));
+ assertEquals(4, keys[i].length());
+ }
+ }
+
+ public void testName() {
+ assertEquals("mock", pref.name());
+
+ pref = new MockAbstractPreferences(pref, " ");
+ assertEquals(" ", pref.name());
+ }
+
+ public void testCharCase() throws BackingStoreException {
+ assertSame(pref.node("samechild"), pref.node("samechild"));
+ assertNotSame(pref.node("sameChild"), pref.node("samechild"));
+ assertNotSame(pref.node("child"), pref.node("Child"));
+ assertNotSame(pref.node("child"), pref.node("Child"));
+ assertNotSame(pref.node("child"), pref.node(" child"));
+ String[] names = pref.childrenNames();
+ assertEquals(5, names.length);
+ for (int i = 0; i < names.length; i++) {
+ String name = names[i];
+ assertTrue("samechild".equals(name) || "sameChild".equals(name)
+ || "child".equals(name) || "Child".equals(name)
+ || " child".equals(name));
+ }
+
+ Preferences mock1 = pref.node("mock1");
+ mock1.put("key", "1value");
+ mock1.put("KEY", "2value");
+ mock1.put("/K/E/Y", "7value");
+ mock1.put("/K/E\\Y\\abc~@!#$%^&*(\\", "8value");
+
+ assertEquals("8value", mock1.get("/K/E\\Y\\abc~@!#$%^&*(\\", null));
+ assertNull(mock1.get("/k/e/y", null));
+ assertEquals("7value", mock1.get("/K/E/Y", null));
+ assertEquals("1value", mock1.get("key", null));
+
+ String[] keys = mock1.keys();
+ assertEquals(4, keys.length);
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i];
+ assertTrue("key".equals(key) || "KEY".equals(key)
+ || "/K/E/Y".equals(key)
+ || "/K/E\\Y\\abc~@!#$%^&*(\\".equals(key));
+ }
+ }
+
+ public void testNode() throws BackingStoreException {
+ try {
+ pref.node(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.node("/java/util/prefs/");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.node("/java//util/prefs");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.node(longName + "a");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ assertNotNull(pref.node(longName));
+
+ assertSame(root, pref.node("/"));
+
+ Preferences prefs = pref.node("/java/util/prefs");
+ assertSame(prefs, parent);
+
+ assertSame(pref, pref.node(""));
+
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences child = (MockAbstractPreferences) ((MockAbstractPreferences) pref)
+ .publicChildSpi("child");
+ assertSame(child, pref.node("child"));
+
+ Preferences child2 = pref.node("child2");
+ assertSame(child2, ((MockAbstractPreferences) pref)
+ .publicChildSpi("child2"));
+
+ Preferences grandchild = pref.node("child/grandchild");
+ assertSame(grandchild, child.childSpi("grandchild"));
+ assertSame(grandchild, child.cachedChildrenImpl()[0]);
+ grandchild.removeNode();
+ assertNotSame(grandchild, pref.node("child/grandchild"));
+
+ grandchild = pref.node("child3/grandchild");
+ AbstractPreferences[] childs = ((MockAbstractPreferences) pref)
+ .cachedChildrenImpl();
+ Preferences child3 = child;
+ for (int i = 0; i < childs.length; i++) {
+ if (childs[i].name().equals("child3")) {
+ child3 = childs[i];
+ break;
+ }
+ }
+ assertSame(child3, grandchild.parent());
+ }
+
+ public void testNodeExists() throws BackingStoreException {
+ try {
+ pref.nodeExists(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.nodeExists("/java/util/prefs/");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.nodeExists("/java//util/prefs");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertTrue(pref.nodeExists("/"));
+
+ assertTrue(pref.nodeExists("/java/util/prefs"));
+
+ assertTrue(pref.nodeExists(""));
+
+ assertFalse(pref.nodeExists("child"));
+ Preferences grandchild = pref.node("child/grandchild");
+ assertTrue(pref.nodeExists("child"));
+ assertTrue(pref.nodeExists("child/grandchild"));
+ grandchild.removeNode();
+ assertTrue(pref.nodeExists("child"));
+ assertFalse(pref.nodeExists("child/grandchild"));
+ assertFalse(grandchild.nodeExists(""));
+
+ assertFalse(pref.nodeExists("child2/grandchild"));
+ pref.node("child2/grandchild");
+ assertTrue(pref.nodeExists("child2/grandchild"));
+ }
+
+ public void testParent() {
+ assertSame(parent, pref.parent());
+ AbstractPreferences child1 = new MockAbstractPreferences(pref, "child1");
+ assertSame(pref, child1.parent());
+ assertNull(root.parent());
+ }
+
+ public void testPut() throws BackingStoreException {
+ pref.put("", "emptyvalue");
+ assertEquals("emptyvalue", pref.get("", null));
+ pref.put("testPutkey", "value1");
+ assertEquals("value1", pref.get("testPutkey", null));
+ pref.put("testPutkey", "value2");
+ assertEquals("value2", pref.get("testPutkey", null));
+
+ pref.put("", "emptyvalue");
+ assertEquals("emptyvalue", pref.get("", null));
+
+ try {
+ pref.put(null, "value");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.put("key", null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.put(longKey, longValue);
+ try {
+ pref.put(longKey + 1, longValue);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.put(longKey, longValue + 1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ pref.removeNode();
+ try {
+ pref.put(longKey, longValue + 1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ pref.put(longKey, longValue);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ }
+
+ public void testPutBoolean() {
+ try {
+ pref.putBoolean(null, false);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putBoolean(longKey, false);
+ try {
+ pref.putBoolean(longKey + "a", false);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putBoolean("testPutBooleanKey", false);
+ assertEquals("false", pref.get("testPutBooleanKey", null));
+ assertFalse(pref.getBoolean("testPutBooleanKey", true));
+ }
+
+ public void testPutDouble() {
+ try {
+ pref.putDouble(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putDouble(longKey, 3);
+ try {
+ pref.putDouble(longKey + "a", 3);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putDouble("testPutDoubleKey", 3);
+ assertEquals("3.0", pref.get("testPutDoubleKey", null));
+ assertEquals(3, pref.getDouble("testPutDoubleKey", 0), 0);
+ }
+
+ public void testPutFloat() {
+ try {
+ pref.putFloat(null, 3f);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putFloat(longKey, 3f);
+ try {
+ pref.putFloat(longKey + "a", 3f);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putFloat("testPutFloatKey", 3f);
+ assertEquals("3.0", pref.get("testPutFloatKey", null));
+ assertEquals(3f, pref.getFloat("testPutFloatKey", 0), 0);
+ }
+
+ public void testPutInt() {
+ try {
+ pref.putInt(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putInt(longKey, 3);
+ try {
+ pref.putInt(longKey + "a", 3);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putInt("testPutIntKey", 3);
+ assertEquals("3", pref.get("testPutIntKey", null));
+ assertEquals(3, pref.getInt("testPutIntKey", 0));
+ }
+
+ public void testPutLong() {
+ try {
+ pref.putLong(null, 3L);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putLong(longKey, 3L);
+ try {
+ pref.putLong(longKey + "a", 3L);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putLong("testPutLongKey", 3L);
+ assertEquals("3", pref.get("testPutLongKey", null));
+ assertEquals(3L, pref.getLong("testPutLongKey", 0));
+ }
+
+ public void testRemove() throws BackingStoreException {
+ pref.remove("key");
+
+ pref.put("key", "value");
+ assertEquals("value", pref.get("key", null));
+ pref.remove("key");
+ assertNull(pref.get("key", null));
+
+ pref.remove("key");
+
+ try {
+ pref.remove(null);
+ } catch (NullPointerException e) {
+ }
+
+ pref.removeNode();
+ try {
+ pref.remove("key");
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ }
+
+ public void testRemoveNode() throws BackingStoreException {
+ Preferences child = pref.node("child");
+ Preferences child1 = pref.node("child1");
+ Preferences grandchild = child.node("grandchild");
+
+ pref.removeNode();
+
+ assertFalse(child.nodeExists(""));
+ assertFalse(child1.nodeExists(""));
+ assertFalse(grandchild.nodeExists(""));
+ assertFalse(pref.nodeExists(""));
+ }
+
+ public void testAddNodeChangeListener() throws BackingStoreException {
+ try {
+ pref.addNodeChangeListener(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ Preferences child1 = null;
+ Preferences child2 = null;
+ Preferences child3 = null;
+ // To get existed node doesn't create the change event
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock1");
+ assertEquals(1, nl.getAdded());
+ nl.reset();
+ child2 = pref.node("mock1");
+ assertEquals(0, nl.getAdded());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ child1.removeNode();
+ }
+ // same listener can be added twice, and must be removed twice
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock2");
+ assertEquals(2, nl.getAdded());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ pref.removeNodeChangeListener(nl);
+ child1.removeNode();
+ }
+ // test remove event
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock3");
+ child1.removeNode();
+ assertEquals(1, nl.getRemoved());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ }
+ // test remove event with two listeners
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock6");
+ child1.removeNode();
+ assertEquals(2, nl.getRemoved());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ pref.removeNodeChangeListener(nl);
+ }
+ // test add/remove indirect children, or remove several children at the
+ // same time
+ try {
+ nl = new MockNodeChangeListener();
+ child1 = pref.node("mock4");
+ child1.addNodeChangeListener(nl);
+ child2 = pref.node("mock4/mock5");
+ assertEquals(1, nl.getAdded());
+ nl.reset();
+ child3 = pref.node("mock4/mock5/mock6");
+ assertEquals(0, nl.getAdded());
+ nl.reset();
+
+ child3.removeNode();
+ assertEquals(0, nl.getRemoved());
+ nl.reset();
+
+ child3 = pref.node("mock4/mock7");
+ assertEquals(1, nl.getAdded());
+ nl.reset();
+
+ child1.removeNode();
+ assertEquals(2, nl.getRemoved());
+ nl.reset();
+ } finally {
+ try {
+ child1.removeNode();
+ } catch (Exception e) {
+ }
+ }
+
+ }
+
+ public void testAddPreferenceChangeListener() {
+ // TODO: start from here
+ try {
+ pref.addPreferenceChangeListener(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ // To get existed node doesn't create the change event
+ try {
+ pl = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(pl);
+ pref.putInt("mock1", 123);
+ assertEquals(1, pl.getChanged());
+ pref.putLong("long_key", Long.MAX_VALUE);
+ assertEquals(2, pl.getChanged());
+ pl.reset();
+
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ //child1.removeNode();
+ }
+
+ // same listener can be added twice, and must be removed twice
+ try {
+ pl = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(pl);
+ pref.addPreferenceChangeListener(pl);
+ pref.putFloat("float_key", Float.MIN_VALUE);
+ assertEquals(2, pl.getChanged());
+ pl.reset();
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ pref.removePreferenceChangeListener(pl);
+
+ }
+ }
+
+ public void testRemoveNodeChangeListener() {
+ try {
+ pref.removeNodeChangeListener(null);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ MockNodeChangeListener l1 = new MockNodeChangeListener();
+ MockNodeChangeListener l2 = new MockNodeChangeListener();
+ pref.addNodeChangeListener(l1);
+ pref.addNodeChangeListener(l1);
+
+ pref.removeNodeChangeListener(l1);
+ pref.removeNodeChangeListener(l1);
+ try {
+ pref.removeNodeChangeListener(l1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.removeNodeChangeListener(l2);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public void testRemovePreferenceChangeListener() {
+ try {
+ pref.removePreferenceChangeListener(null);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ MockPreferenceChangeListener l1 = new MockPreferenceChangeListener();
+ MockPreferenceChangeListener l2 = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(l1);
+ pref.addPreferenceChangeListener(l1);
+ try {
+ pref.removePreferenceChangeListener(l2);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.removePreferenceChangeListener(l1);
+ pref.removePreferenceChangeListener(l1);
+ try {
+ pref.removePreferenceChangeListener(l1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ }
+
+ public void testSync() throws BackingStoreException {
+
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ p.resetSyncTimes();
+ p.sync();
+ assertEquals(1, p.getSyncTimes());
+
+ p.resetSyncTimes();
+ MockAbstractPreferences child = (MockAbstractPreferences) p
+ .node("child");
+ MockAbstractPreferences child2 = new MockAbstractPreferences(p,
+ "child2");
+ p.childs.put("child2", child2);
+ assertEquals(1, p.cachedChildrenImpl().length);
+ assertSame(child, p.cachedChildrenImpl()[0]);
+ p.sync();
+ assertEquals(1, p.getSyncTimes());
+ assertEquals(1, child.getSyncTimes());
+ assertEquals(0, child2.getSyncTimes());
+
+ p.resetSyncTimes();
+ child.resetSyncTimes();
+ child.sync();
+ assertEquals(0, p.getSyncTimes());
+ assertEquals(1, child.getSyncTimes());
+
+ p.resetSyncTimes();
+ child.resetSyncTimes();
+ MockAbstractPreferences grandson = (MockAbstractPreferences) child
+ .node("grandson");
+ child.sync();
+ assertEquals(0, p.getSyncTimes());
+ assertEquals(1, child.getSyncTimes());
+ assertEquals(1, grandson.getSyncTimes());
+ }
+
+ public void testFlush() throws BackingStoreException {
+
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ p.resetFlushedTimes();
+ p.flush();
+ assertEquals(1, p.getFlushedTimes());
+
+ p.resetFlushedTimes();
+ MockAbstractPreferences child = (MockAbstractPreferences) p
+ .node("child");
+ MockAbstractPreferences child2 = new MockAbstractPreferences(p,
+ "child2");
+ p.childs.put("child2", child2);
+ assertEquals(1, p.cachedChildrenImpl().length);
+ assertSame(child, p.cachedChildrenImpl()[0]);
+ p.flush();
+ assertEquals(1, p.getFlushedTimes());
+ assertEquals(1, child.getFlushedTimes());
+ assertEquals(0, child2.getFlushedTimes());
+
+ p.resetFlushedTimes();
+ child.resetFlushedTimes();
+ child.flush();
+ assertEquals(0, p.getFlushedTimes());
+ assertEquals(1, child.getFlushedTimes());
+
+ p.resetFlushedTimes();
+ child.resetFlushedTimes();
+ MockAbstractPreferences grandson = (MockAbstractPreferences) child
+ .node("grandson");
+ child.flush();
+ assertEquals(0, p.getFlushedTimes());
+ assertEquals(1, child.getFlushedTimes());
+ assertEquals(1, grandson.getFlushedTimes());
+
+ p.resetFlushedTimes();
+ child.resetFlushedTimes();
+ grandson.resetFlushedTimes();
+ child.removeNode();
+ child.flush();
+ assertEquals(0, p.getFlushedTimes());
+ assertEquals(1, child.getFlushedTimes());
+ assertEquals(0, grandson.getFlushedTimes());
+ }
+
+ public void testGetChild() throws BackingStoreException {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ assertNull(p.getChildImpl("child"));
+ MockAbstractPreferences child = new MockAbstractPreferences(p, "child");
+ p.childs.put("child", child);
+ assertSame(child, p.getChildImpl("child"));
+ assertNull(p.getChildImpl("child "));
+
+ assertNull(p.getChildImpl("child/grandson"));
+ child.childs.put("grandson", new MockAbstractPreferences(child,
+ "grandson"));
+ assertNull(p.getChildImpl("child/grandson"));
+
+ assertNull(p.getChildImpl(null));
+ assertNull(p.getChildImpl(""));
+ assertNull(p.getChildImpl(" "));
+ assertNull(p.getChildImpl("abc//abc"));
+ assertNull(p.getChildImpl("child/"));
+ assertNull(p.getChildImpl(longName + "a"));
+
+ child.removeNode();
+ assertNull(p.getChildImpl("child"));
+ }
+
+ public void testIsRemoved() throws BackingStoreException {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ assertFalse(p.isRemovedImpl());
+ p.removeNode();
+ assertTrue(p.isRemovedImpl());
+ }
+
+ public void testExportNode() throws Exception {
+ try {
+ pref.exportNode(null);
+ fail();
+ } catch (NullPointerException e) {
+ // Expected
+ }
+
+ pref.putBoolean("key", false);
+ Preferences child = pref.node("child<");
+ child.put("key2", "value2<");
+ Preferences grandson = child.node("grandson");
+ grandson.put("key3", "value3");
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ child.exportNode(out);
+
+ byte[] result = out.toString().getBytes();
+ ByteArrayInputStream in = new ByteArrayInputStream(result);
+
+ try {
+ parseXmlStream(in, true);
+ } catch (Exception ee) {
+ fail("Exception " + ee + " does not expected");
+ }
+ }
+
+ private static Document parseXmlStream(InputStream input, boolean validating)
+ throws SAXException, IOException, ParserConfigurationException {
+ // Create a builder factory
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ factory.setValidating(validating);
+
+ // Create the builder and parse the file
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document doc = builder.parse(input);
+ return doc;
+ }
+
+ public void testExportSubtree() throws Exception {
+ try {
+ pref.exportSubtree(null);
+ fail();
+ } catch (NullPointerException e) {
+ // Expected
+ }
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ pref.putBoolean("key", false);
+ Preferences child = pref.node("child");
+ child.put("key2", "value2");
+ Preferences grandson = child.node("grandson");
+ grandson.put("key3", "value3");
+ child.node("grandson2");
+ Preferences grandgrandson = grandson.node("grandgrandson");
+ grandgrandson.put("key4", "value4");
+ child.exportSubtree(out);
+
+ byte[] result = out.toByteArray();
+ // System.out.println(new String(result, "utf-8"));
+ ByteArrayInputStream in = new ByteArrayInputStream(result);
+
+ try {
+ parseXmlStream(in, true);
+ } catch (Exception ee) {
+ fail("Exception " + ee + " does not expected");
+ }
+ }
+
+ public void testCachedChildren() throws Exception {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ assertEquals(0, p.cachedChildrenImpl().length);
+
+ MockAbstractPreferences child = (MockAbstractPreferences) p
+ .getChildImpl("child");
+ assertNull(child);
+
+ child = new MockAbstractPreferences(p, "child");
+ assertSame(child, p.getChildImpl("child"));
+
+ assertEquals(0, p.cachedChildrenImpl().length);
+
+ p.node("child");
+ assertSame(child, p.cachedChildrenImpl()[0]);
+
+ MockAbstractPreferences grandchild = new MockAbstractPreferences(child,
+ "grandchild");
+ assertSame(grandchild, child.getChildImpl("grandchild"));
+ assertNull(p.getChildImpl("grandchild"));
+
+ assertEquals(1, p.cachedChildrenImpl().length);
+ assertEquals(0, child.cachedChildrenImpl().length);
+
+ p.node("child/grandchild");
+ assertSame(child, p.cachedChildrenImpl()[0]);
+ assertSame(grandchild, child.cachedChildrenImpl()[0]);
+ assertEquals(1, p.cachedChildrenImpl().length);
+ assertEquals(1, child.cachedChildrenImpl().length);
+
+ p.childs.put("child2", new MockAbstractPreferences(p, "child2"));
+ p.nodeExists("child2/grandchild");
+ assertSame(child, p.cachedChildrenImpl()[0]);
+ assertSame(grandchild, child.cachedChildrenImpl()[0]);
+ assertEquals(1, p.cachedChildrenImpl().length);
+ assertEquals(1, child.cachedChildrenImpl().length);
+ }
+
+ public void testAbstractMethod() {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ ((MockAbstractPreferences) pref).protectedAbstractMethod();
+ }
+
+ public Object invokeNonPublicMethod(AbstractPreferences obj, String name,
+ Class[] params, Object[] paramValues) throws SecurityException,
+ NoSuchMethodException, IllegalArgumentException,
+ IllegalAccessException, InvocationTargetException {
+ Method method = obj.getClass().getMethod(name, params);
+ method.setAccessible(true);
+ return method.invoke(obj, paramValues);
+ }
+
+ public void testBackingStoreException() throws IOException,
+ BackingStoreException {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ p.setResult(MockAbstractPreferences.backingException);
+ try {
+ p.childrenNames();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ p.put("exceptionkey", "value");
+ p.absolutePath();
+ p.toString();
+ assertEquals("exception default", p.get("key", "exception default"));
+ p.remove("key");
+ try {
+ p.clear();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ p.putInt("key", 3);
+ p.getInt("key", 3);
+ p.putLong("key", 3l);
+ p.getLong("key", 3l);
+ p.putDouble("key", 3);
+ p.getDouble("key", 3);
+ p.putBoolean("key", true);
+ p.getBoolean("key", true);
+ p.putFloat("key", 3f);
+ p.getFloat("key", 3f);
+ p.putByteArray("key", new byte[0]);
+ p.getByteArray("key", new byte[0]);
+ try {
+ p.keys();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+
+ try {
+ p.keys();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ try {
+ p.childrenNames();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ p.parent();
+ p.node("");
+ p.nodeExists("");
+ try {
+ p.removeNode();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ p.name();
+ p.absolutePath();
+ p.isUserNode();
+ MockPreferenceChangeListener mockPreferenceChangeListener = new MockPreferenceChangeListener();
+ p.addPreferenceChangeListener(mockPreferenceChangeListener);
+ p.removePreferenceChangeListener(mockPreferenceChangeListener);
+ MockNodeChangeListener mockNodeChangeListener = new MockNodeChangeListener();
+ p.addNodeChangeListener(mockNodeChangeListener);
+ p.removeNodeChangeListener(mockNodeChangeListener);
+ p.toString();
+ try {
+ p.sync();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ try {
+ p.flush();
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ try {
+ p.exportNode(new ByteArrayOutputStream());
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ try {
+ p.exportSubtree(new ByteArrayOutputStream());
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ p.isRemovedImpl();
+ try {
+ p.getChildImpl(null);
+ fail();
+ } catch (BackingStoreException e) {
+ }
+ p.cachedChildrenImpl();
+ }
+
+ public void testRuntimeException() throws IOException,
+ BackingStoreException {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ p.setResult(MockAbstractPreferences.runtimeException);
+ try {
+ p.childrenNames();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.put("exceptionkey", "value");
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.absolutePath();
+ p.toString();
+ assertEquals("exception default", p.get("key", "exception default"));
+ try {
+ p.remove("key");
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.clear();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.putInt("key", 3);
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.getInt("key", 3);
+ try {
+ p.putLong("key", 3l);
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.getLong("key", 3l);
+ try {
+ p.putDouble("key", 3);
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.getDouble("key", 3);
+ try {
+ p.putBoolean("key", true);
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.getBoolean("key", true);
+ try {
+ p.putFloat("key", 3f);
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.getFloat("key", 3f);
+ try {
+ p.putByteArray("key", new byte[0]);
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.getByteArray("key", new byte[0]);
+ try {
+ p.keys();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.keys();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.childrenNames();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.parent();
+ p.node("");
+ p.nodeExists("");
+ try {
+ p.removeNode();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.name();
+ p.absolutePath();
+ p.isUserNode();
+ MockPreferenceChangeListener pcl = new MockPreferenceChangeListener();
+ p.addPreferenceChangeListener(pcl);
+ p.removePreferenceChangeListener(pcl);
+ MockNodeChangeListener ncl = new MockNodeChangeListener();
+ p.addNodeChangeListener(ncl);
+ p.removeNodeChangeListener(ncl);
+ p.toString();
+ try {
+ p.sync();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.flush();
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.exportNode(new ByteArrayOutputStream());
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ try {
+ p.exportSubtree(new ByteArrayOutputStream());
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.isRemovedImpl();
+ try {
+ p.getChildImpl(null);
+ fail();
+ } catch (MockRuntimeException e) {
+ }
+ p.cachedChildrenImpl();
+ }
+
+ public void testSPIReturnNull() throws IOException, BackingStoreException {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ p.setResult(MockAbstractPreferences.returnNull);
+ try {
+ p.childrenNames();
+ fail();
+ } catch (NullPointerException e) {
+ }
+ p.absolutePath();
+ p.toString();
+ p.put("nullkey", "value");
+ assertEquals("null default", p.get("key", "null default"));
+ p.remove("key");
+ try {
+ p.clear();
+ fail();
+ } catch (NullPointerException e) {
+ }
+ p.putInt("key", 3);
+ p.getInt("key", 3);
+ p.putLong("key", 3l);
+ p.getLong("key", 3l);
+ p.putDouble("key", 3);
+ p.getDouble("key", 3);
+ p.putBoolean("key", true);
+ p.getBoolean("key", true);
+ p.putFloat("key", 3f);
+ p.getFloat("key", 3f);
+ p.putByteArray("key", new byte[0]);
+ p.getByteArray("key", new byte[0]);
+ p.keys();
+ try {
+ p.childrenNames();
+ fail();
+ } catch (NullPointerException e) {
+ }
+ p.parent();
+ p.node("");
+ p.nodeExists("");
+ try {
+ p.removeNode();
+ fail();
+ } catch (NullPointerException e) {
+ }
+ p.name();
+ p.absolutePath();
+ p.isUserNode();
+ MockPreferenceChangeListener mockPreferenceChangeListener = new MockPreferenceChangeListener();
+ p.addPreferenceChangeListener(mockPreferenceChangeListener);
+ p.removePreferenceChangeListener(mockPreferenceChangeListener);
+ MockNodeChangeListener mockNodeChangeListener = new MockNodeChangeListener();
+ p.addNodeChangeListener(mockNodeChangeListener);
+ p.removeNodeChangeListener(mockNodeChangeListener);
+ p.toString();
+ p.sync();
+ p.flush();
+ try {
+ p.exportNode(System.out);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.exportSubtree(System.out);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ p.isRemovedImpl();
+ try {
+ p.getChildImpl("");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ p.cachedChildrenImpl();
+ }
+
+ public void testIllegalStateException() throws IOException,
+ BackingStoreException {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ pref.removeNode();
+ // after remove node, every methods, except name(), absolutePath(),
+ // isUserNode(), flush() or nodeExists(""),
+ // will throw illegal state exception
+ pref.nodeExists("");
+ pref.name();
+ pref.absolutePath();
+ pref.isUserNode();
+ pref.toString();
+ pref.flush();
+ try {
+ pref.nodeExists("child");
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.childrenNames();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.remove(null);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.clear();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.get("key", "null default");
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.put("nullkey", "value");
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.putInt("key", 3);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.getInt("key", 3);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.putLong("key", 3l);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.getLong("key", 3l);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.putDouble("key", 3);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.getDouble("key", 3);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.putBoolean("key", true);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.getBoolean("key", true);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.putFloat("key", 3f);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.getFloat("key", 3f);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.putByteArray("key", new byte[0]);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.getByteArray("key", new byte[0]);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.keys();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.keys();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.childrenNames();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.parent();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.node(null);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.removeNode();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref
+ .addPreferenceChangeListener(new MockPreferenceChangeListener());
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref
+ .removePreferenceChangeListener(new MockPreferenceChangeListener());
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.addNodeChangeListener(new MockNodeChangeListener());
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.removeNodeChangeListener(new MockNodeChangeListener());
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.sync();
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.exportNode(null);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.exportSubtree(null);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ p.isRemovedImpl();
+ p.cachedChildrenImpl();
+ try {
+ p.getChildImpl(null);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ }
+
+ public void testNullAndIllegalStateException() throws Exception {
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences p = (MockAbstractPreferences) pref;
+ p.removeNode();
+ try {
+ p.get(null, "null default");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.put(null, "value");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.putInt(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.getInt(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.putLong(null, 3l);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.getLong(null, 3l);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.putDouble(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.getDouble(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.putBoolean(null, true);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.getBoolean(null, true);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.putFloat(null, 3f);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.getFloat(null, 3f);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.putByteArray(null, new byte[0]);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.getByteArray(null, new byte[0]);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.addPreferenceChangeListener(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.removePreferenceChangeListener(null);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ p.addNodeChangeListener(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ p.removeNodeChangeListener(null);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#childrenNamesSpi()
+ *
+ */
+ public void testChildrenNamesSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ try {
+ assertEquals(0, p.childrenNamesSpi().length);
+ } catch(java.util.prefs.BackingStoreException bse) {
+ fail("java.util.prefs.BackingStoreException is thrown: " +
+ bse.toString());
+ }
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#childSpi()
+ *
+ */
+
+ public void testChildSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ Preferences child = p.node("mock1");
+ assertEquals(child, p.childSpi("mock1"));
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#flushSpi()
+ *
+ */
+
+ public void testFlushSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ try {
+ p.flushSpi();
+ } catch(Exception e) {
+ fail("Unexpected exception was thrown: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#getSpi()
+ *
+ */
+
+ public void testGetSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ try {
+ assertNull(p.getSpi(""));
+ p.put("key", "default");
+ assertEquals("default", p.getSpi("key"));
+ } catch(Exception e) {
+ fail("Unexpected exception was thrown: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#keysSpi()
+ *
+ */
+
+ public void testKeysSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ try {
+ p.put("key1", "default");
+ p.putInt("key2", 123);
+ assertEquals(2, p.keysSpi().length);
+ assertEquals("key2", p.keysSpi()[0]);
+ assertEquals("key1", p.keysSpi()[1]);
+ } catch(Exception e) {
+ fail("Unexpected exception was thrown: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#putSpi()
+ *
+ */
+
+ public void testPutSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ try {
+ p.putSpi("key1", "default");
+ p.putSpi("key2", "123");
+ assertEquals(2, p.keysSpi().length);
+ assertEquals("key2", p.keysSpi()[0]);
+ assertEquals("key1", p.keysSpi()[1]);
+ } catch(Exception e) {
+ fail("Unexpected exception was thrown: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#removeSpi()
+ *
+ */
+
+ public void testRemoveSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ p.put("key1", "value1");
+ try {
+ p.removeSpi("key1");
+
+ assertNull(p.getSpi("key1"));
+ } catch(Exception e) {
+ fail("Unexpected exception was thrown: " + e.getMessage());
+ }
+ }
+
+ /**
+ * @test java.util.prefs.AbstractPreferences#syncSpi()
+ *
+ */
+
+ public void testSyncSpi() {
+ MockAbstractPreferences p = new MockAbstractPreferences(
+ (AbstractPreferences) Preferences.userRoot(), "mock");
+ p.put("key1", "value1");
+ try {
+ p.syncSpi();
+ } catch(Exception e) {
+ fail("Unexpected exception was thrown: " + e.getMessage());
+ }
+ }
+
+ /**
+ * Regression for HARMONY-828
+ */
+ public void testLongPath() throws Exception {
+ assertFalse(pref
+ .nodeExists("ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd"));
+ }
+
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AllTests.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AllTests.java
new file mode 100644
index 0000000..cbcc77c
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AllTests.java
@@ -0,0 +1,48 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Suite for package org.apache.harmony.prefs.tests.java.util.prefs
+ *
+ */
+public class AllTests {
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(AllTests.suite());
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("Suite for org.apache.harmony.prefs.tests.java.util.prefs");
+ // $JUnit-BEGIN$
+ suite.addTestSuite(NodeChangeListenerTest.class);
+ suite.addTestSuite(PreferenceChangeListenerTest.class);
+ suite.addTestSuite(PreferencesFactoryTest.class);
+ suite.addTestSuite(BackingStoreExceptionTest.class);
+ suite.addTestSuite(InvalidPreferencesFormatExceptionTest.class);
+ suite.addTestSuite(PreferenceChangeEventTest.class);
+ suite.addTestSuite(NodeChangeEventTest.class);
+ suite.addTestSuite(PreferencesTest.class);
+ suite.addTestSuite(AbstractPreferencesTest.class);
+ suite.addTestSuite(FilePreferencesImplTest.class);
+ // $JUnit-END$
+ return suite;
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.java
new file mode 100644
index 0000000..5035a1b
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.java
@@ -0,0 +1,66 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.BackingStoreException;
+
+import junit.framework.TestCase;
+
+import org.apache.harmony.testframework.serialization.SerializationTest;
+
+/**
+ *
+ *
+ */
+public class BackingStoreExceptionTest extends TestCase {
+
+ /*
+ * Class under test for void BackingStoreException(String)
+ */
+ public void testBackingStoreExceptionString() {
+ BackingStoreException e = new BackingStoreException("msg");
+ assertNull(e.getCause());
+ assertEquals("msg", e.getMessage());
+ }
+
+ /*
+ * Class under test for void BackingStoreException(Throwable)
+ */
+ public void testBackingStoreExceptionThrowable() {
+ Throwable t = new Throwable("msg");
+ BackingStoreException e = new BackingStoreException(t);
+ assertTrue(e.getMessage().indexOf(t.getClass().getName()) >= 0);
+ assertTrue(e.getMessage().indexOf("msg") >= 0);
+ assertEquals(t, e.getCause());
+ }
+
+ /**
+ * @tests serialization/deserialization.
+ */
+ public void testSerializationSelf() throws Exception {
+
+ SerializationTest.verifySelf(new BackingStoreException("msg"));
+ }
+
+ /**
+ * @tests serialization/deserialization compatibility with RI.
+ */
+ public void testSerializationCompatibility() throws Exception {
+
+ SerializationTest.verifyGolden(this, new BackingStoreException("msg"));
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java
new file mode 100644
index 0000000..026c813
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java
@@ -0,0 +1,214 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.io.FilePermission;
+import java.io.IOException;
+import java.security.Permission;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.Preferences;
+
+import junit.framework.TestCase;
+
+public class FilePreferencesImplTest extends TestCase {
+
+ private String prevFactory;
+ private Preferences uroot;
+ private Preferences sroot;
+
+ public FilePreferencesImplTest() {
+ super();
+ }
+
+ protected void setUp() throws Exception {
+ // prevFactory = System.getProperty("java.util.prefs.PreferencesFactory");
+ // System.setProperty("java.util.prefs.PreferencesFactory", "java.util.prefs.FilePreferencesFactoryImpl");
+
+ // uroot = (AbstractPreferences) Preferences.userRoot();
+ uroot = Preferences.userRoot();
+ sroot = Preferences.systemRoot();
+ }
+
+ protected void tearDown() throws Exception {
+ if (prevFactory != null)
+ System.setProperty("java.util.prefs.PreferencesFactory", prevFactory);
+
+ uroot = null;
+ sroot = null;
+ }
+
+ public void testPutGet() throws IOException, BackingStoreException {
+ uroot.put("ukey1", "value1");
+ assertEquals("value1", uroot.get("ukey1", null));
+ String[] names = uroot.keys();
+ assertTrue(names.length >= 1);
+
+ uroot.put("ukey2", "value3");
+ assertEquals("value3", uroot.get("ukey2", null));
+ uroot.put("\u4e2d key1", "\u4e2d value1");
+ assertEquals("\u4e2d value1", uroot.get("\u4e2d key1", null));
+ names = uroot.keys();
+ assertEquals(3, names.length);
+
+ uroot.clear();
+ names = uroot.keys();
+ assertEquals(0, names.length);
+
+ sroot.put("skey1", "value1");
+ assertEquals("value1", sroot.get("skey1", null));
+ sroot.put("\u4e2d key1", "\u4e2d value1");
+ assertEquals("\u4e2d value1", sroot.get("\u4e2d key1", null));
+ }
+
+ public void testChildNodes() throws Exception {
+ Preferences child1 = uroot.node("child1");
+ Preferences child2 = uroot.node("\u4e2d child2");
+ Preferences grandchild = child1.node("grand");
+ assertNotNull(grandchild);
+
+ String[] childNames = uroot.childrenNames();
+ assertEquals(4, childNames.length);
+ for (int i = 0; i < childNames.length; i++) {
+ System.out.println(childNames[i]);
+ }
+
+ childNames = child1.childrenNames();
+ assertEquals(1, childNames.length);
+ for (int i = 0; i < childNames.length; i++) {
+ System.out.println(childNames[i]);
+ }
+
+ childNames = child2.childrenNames();
+ assertEquals(0, childNames.length);
+ for (int i = 0; i < childNames.length; i++) {
+ System.out.println(childNames[i]);
+ }
+
+ child1.removeNode();
+ childNames = uroot.childrenNames();
+ assertEquals(3, childNames.length);
+ for (int i = 0; i < childNames.length; i++) {
+ System.out.println(childNames[i]);
+ }
+ // child2.removeNode();
+ // childNames = uroot.childrenNames();
+ // assertEquals(0, childNames.length);
+
+ child1 = sroot.node("child1");
+ child2 = sroot.node("child2");
+ grandchild = child1.node("grand");
+
+ childNames = sroot.childrenNames();
+
+ for (int i = 0; i < childNames.length; i++) {
+ System.out.println(childNames[i]);
+ }
+ // assertEquals(2, childNames.length);
+
+ childNames = child1.childrenNames();
+ assertEquals(1, childNames.length);
+ for (int i = 0; i < childNames.length; i++) {
+ System.out.println(childNames[i]);
+ }
+
+ childNames = child2.childrenNames();
+ assertEquals(0, childNames.length);
+ for (int i = 0; i < childNames.length; i++) {
+ System.out.println(childNames[i]);
+ }
+ }
+
+ public void testSecurityException() throws BackingStoreException {
+ Preferences child1 = uroot.node("child1");
+ MockFileSecurityManager manager = new MockFileSecurityManager();
+ manager.install();
+ try {
+ try {
+ uroot.node("securityNode");
+ fail("should throw security exception");
+ } catch (SecurityException e) {
+ }
+ try {
+ // need FilePermission(delete);
+ child1.removeNode();
+ fail("should throw security exception");
+ } catch (SecurityException e) {
+ }
+ try {
+ uroot.childrenNames();
+ fail("should throw security exception");
+ } catch (SecurityException e) {
+ }
+ uroot.keys();
+ uroot.put("securitykey", "value1");
+ uroot.remove("securitykey");
+ try {
+ uroot.flush();
+ fail("should throw security exception");
+ } catch (SecurityException e) {
+ } catch (BackingStoreException e) {
+ assertTrue(e.getCause() instanceof SecurityException);
+ }
+ try {
+ uroot.sync();
+ fail("should throw security exception");
+ } catch (SecurityException e) {
+ } catch (BackingStoreException e) {
+ assertTrue(e.getCause() instanceof SecurityException);
+ }
+ } finally {
+ manager.restoreDefault();
+ }
+ }
+
+ static class MockFileSecurityManager extends SecurityManager {
+
+ SecurityManager dflt;
+
+ public MockFileSecurityManager() {
+ super();
+ dflt = System.getSecurityManager();
+ }
+
+ public void install() {
+ System.setSecurityManager(this);
+ }
+
+ public void restoreDefault() {
+ System.setSecurityManager(dflt);
+ }
+
+ public void checkPermission(Permission perm) {
+ if (perm instanceof FilePermission) {
+ throw new SecurityException();
+ } else if (dflt != null) {
+ dflt.checkPermission(perm);
+ }
+ }
+
+ public void checkPermission(Permission perm, Object ctx) {
+ if (perm instanceof FilePermission) {
+ System.out.println(perm.getActions());
+ throw new SecurityException();
+ } else if (dflt != null) {
+ dflt.checkPermission(perm, ctx);
+ }
+ }
+
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java
new file mode 100644
index 0000000..78ed704
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java
@@ -0,0 +1,83 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.InvalidPreferencesFormatException;
+
+import junit.framework.TestCase;
+
+import org.apache.harmony.testframework.serialization.SerializationTest;
+
+/**
+ *
+ */
+public class InvalidPreferencesFormatExceptionTest extends TestCase {
+
+ /*
+ * Class under test for void InvalidPreferencesFormatException(String)
+ */
+ public void testInvalidPreferencesFormatExceptionString() {
+ InvalidPreferencesFormatException e = new InvalidPreferencesFormatException(
+ "msg");
+ assertNull(e.getCause());
+ assertEquals("msg", e.getMessage());
+ }
+
+ /*
+ * Class under test for void InvalidPreferencesFormatException(String,
+ * Throwable)
+ */
+ public void testInvalidPreferencesFormatExceptionStringThrowable() {
+ Throwable t = new Throwable("root");
+ InvalidPreferencesFormatException e = new InvalidPreferencesFormatException(
+ "msg", t);
+ assertSame(t, e.getCause());
+ assertTrue(e.getMessage().indexOf("root") < 0);
+ assertTrue(e.getMessage().indexOf(t.getClass().getName()) < 0);
+ assertTrue(e.getMessage().indexOf("msg") >= 0);
+ }
+
+ /*
+ * Class under test for void InvalidPreferencesFormatException(Throwable)
+ */
+ public void testInvalidPreferencesFormatExceptionThrowable() {
+ Throwable t = new Throwable("root");
+ InvalidPreferencesFormatException e = new InvalidPreferencesFormatException(
+ t);
+ assertSame(t, e.getCause());
+ assertTrue(e.getMessage().indexOf("root") >= 0);
+ assertTrue(e.getMessage().indexOf(t.getClass().getName()) >= 0);
+ }
+
+ /**
+ * @tests serialization/deserialization.
+ */
+ public void testSerializationSelf() throws Exception {
+
+ SerializationTest.verifySelf(new InvalidPreferencesFormatException(
+ "msg"));
+ }
+
+ /**
+ * @tests serialization/deserialization compatibility with RI.
+ */
+ public void testSerializationCompatibility() throws Exception {
+
+ SerializationTest.verifyGolden(this,
+ new InvalidPreferencesFormatException("msg"));
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java
new file mode 100644
index 0000000..5faca23
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockAbstractPreferences.java
@@ -0,0 +1,245 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+
+public class MockAbstractPreferences extends AbstractPreferences {
+ static final int NORMAL = 0;
+
+ static final int backingException = 1;
+
+ static final int runtimeException = 2;
+
+ static final int returnNull = 3;
+
+ int result = NORMAL;
+
+ Properties attr = new Properties();
+
+ Map<String, MockAbstractPreferences> childs = new HashMap<String, MockAbstractPreferences>();
+
+ private int flushedTimes;
+
+ private int syncTimes;
+
+ protected MockAbstractPreferences(AbstractPreferences parent, String name) {
+ this(parent, name, false);
+
+ }
+
+ protected MockAbstractPreferences(AbstractPreferences parent, String name,
+ boolean newNode) {
+ super(parent, name);
+ super.newNode = newNode;
+ if (parent instanceof MockAbstractPreferences) {
+ ((MockAbstractPreferences) parent).addChild(this);
+ }
+ }
+
+ public int getFlushedTimes() {
+ return flushedTimes;
+ }
+
+ public void resetFlushedTimes() {
+ flushedTimes = 0;
+ }
+
+ public int getSyncTimes() {
+ return syncTimes;
+ }
+
+ public void resetSyncTimes() {
+ syncTimes = 0;
+ }
+
+ private void addChild(MockAbstractPreferences c) {
+ childs.put(c.name(), c);
+ }
+
+ public void setResult(int r) {
+ result = r;
+ }
+
+ public Object lock() {
+ return lock;
+ }
+
+ public String[] childrenNamesSpi() throws BackingStoreException {
+ checkException();
+ if (result == returnNull)
+ return null;
+ String[] r = new String[childs.size()];
+ childs.keySet().toArray(r);
+ return r;
+ }
+
+ private void checkException() throws BackingStoreException {
+ switch (result) {
+ case NORMAL:
+ return;
+ case backingException:
+ throw new BackingStoreException("test");
+ case runtimeException:
+ throw new MockRuntimeException("test");
+ }
+ }
+
+ public AbstractPreferences publicChildSpi(String name) {
+ return childSpi(name);
+ }
+
+ public AbstractPreferences childSpi(String name) {
+ try {
+ checkException();
+ } catch (BackingStoreException e) {
+ }
+ if (result == returnNull)
+ return null;
+ AbstractPreferences r = childs.get(name);
+ if (r == null) {
+ r = new MockAbstractPreferences(this, name, true);
+
+ }
+ return r;
+ }
+
+ public void flushSpi() throws BackingStoreException {
+ checkException();
+ flushedTimes++;
+ }
+
+ public String getSpi(String key) {
+ try {
+ checkException();
+ } catch (BackingStoreException e) {
+ }
+ if (null == key) {
+ return null;
+ }
+ return result == returnNull ? null : attr.getProperty(key);
+ }
+
+ public String[] keysSpi() throws BackingStoreException {
+ checkException();
+ Set<Object> keys = attr.keySet();
+ String[] results = new String[keys.size()];
+ keys.toArray(results);
+ return result == returnNull ? null : results;
+ }
+
+ public void putSpi(String name, String value) {
+ try {
+ checkException();
+ } catch (BackingStoreException e) {
+ }
+ if (name == null || value == null) {
+ return;
+ }
+ attr.put(name, value);
+ }
+
+ protected void removeNodeSpi() throws BackingStoreException {
+ checkException();
+ ((MockAbstractPreferences) parent()).childs.remove(name());
+ }
+
+ public void removeSpi(String key) {
+ try {
+ checkException();
+ } catch (BackingStoreException e) {
+ }
+ if (null == key) {
+ return;
+ }
+ attr.remove(key);
+ }
+
+ public void syncSpi() throws BackingStoreException {
+ checkException();
+ syncTimes++;
+ }
+
+ public boolean getNewNode() {
+ return newNode;
+ }
+
+ public Object getLock() {
+ return lock;
+ }
+
+ public void protectedAbstractMethod() {
+ try {
+ childrenNamesSpi();
+ } catch (BackingStoreException e) {
+ }
+ childSpi("mock");
+ try {
+ flushSpi();
+ } catch (BackingStoreException e1) {
+ }
+ getSpi(null);
+ isRemoved();
+ try {
+ keysSpi();
+ } catch (BackingStoreException e2) {
+ }
+ putSpi(null, null);
+ try {
+ removeNodeSpi();
+ } catch (BackingStoreException e3) {
+ }
+ removeSpi(null);
+ try {
+ syncSpi();
+ } catch (BackingStoreException e4) {
+ }
+ }
+
+ public boolean isRemovedImpl() {
+ return super.isRemoved();
+ }
+
+ public AbstractPreferences getChildImpl(String name)
+ throws BackingStoreException {
+ return super.getChild(name);
+ }
+
+ public AbstractPreferences[] cachedChildrenImpl() {
+ return super.cachedChildren();
+ }
+
+}
+
+class MockRuntimeException extends RuntimeException {
+
+ private static final long serialVersionUID = 1L;
+
+ public MockRuntimeException(String s) {
+ super(s);
+ }
+
+ public MockRuntimeException() {
+ super();
+ }
+}
+
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockNodeChangeListener.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockNodeChangeListener.java
new file mode 100644
index 0000000..7fba914
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockNodeChangeListener.java
@@ -0,0 +1,165 @@
+package org.apache.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.NodeChangeEvent;
+import java.util.prefs.NodeChangeListener;
+import java.util.prefs.Preferences;
+
+public class MockNodeChangeListener implements NodeChangeListener {
+ private boolean addDispatched = false;
+
+ private boolean removeDispatched = false;
+
+ private Object addLock = new Object();
+
+ private Object removeLock = new Object();
+
+ private int added = 0;
+
+ private int removed = 0;
+
+ private int testNum = 0;
+
+ public static final int TEST_GET_CHILD = 1;
+
+ public static final int TEST_GET_PARENT = 2;
+
+ boolean addResult = false;
+
+ boolean removeResult = false;
+
+ public MockNodeChangeListener(int test) {
+ testNum = test;
+ }
+
+ public MockNodeChangeListener() {
+
+ }
+
+ public void childAdded(NodeChangeEvent e) {
+
+ synchronized (addLock) {
+ switch (testNum) {
+ case TEST_GET_CHILD:
+ Preferences child = e.getChild();
+ if (child == null) {
+ addResult = false;
+ } else {
+ if (child.name() == "mock1") {
+ addResult = true;
+ }
+ }
+ break;
+ case TEST_GET_PARENT:
+ Preferences parent = e.getParent();
+ if (parent == null) {
+ addResult = false;
+ } else {
+ if (parent.name() == "mock") {
+ addResult = true;
+ }
+ }
+
+ break;
+ }
+ ++added;
+ addDispatched = true;
+ addLock.notifyAll();
+ }
+ }
+
+ public void childRemoved(NodeChangeEvent e) {
+ synchronized (removeLock) {
+ switch (testNum) {
+ case TEST_GET_CHILD:
+ Preferences child = e.getChild();
+ if (child == null) {
+ removeResult = false;
+ } else {
+ if (child.name() == "mock1") {
+ removeResult = true;
+ }
+ }
+ break;
+ case TEST_GET_PARENT:
+ Preferences parent = e.getParent();
+ if (parent == null) {
+ addResult = false;
+ } else {
+ if (parent.name() == "mock") {
+ addResult = true;
+ }
+ }
+
+ break;
+ }
+ removed++;
+ removeDispatched = true;
+ removeLock.notifyAll();
+ }
+ }
+
+ public boolean getAddResult() {
+ synchronized (addLock) {
+ if (!addDispatched) {
+ try {
+ // TODO: don't know why must add limitation
+ addLock.wait(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ addDispatched = false;
+ }
+ return addResult;
+ }
+
+ public boolean getRemoveResult() {
+ synchronized (removeLock) {
+ if (!removeDispatched) {
+ try {
+ // TODO: don't know why must add limitation
+ removeLock.wait(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ removeDispatched = false;
+ }
+ return removeResult;
+ }
+
+ public int getAdded() {
+ synchronized (addLock) {
+ if (!addDispatched) {
+ try {
+ // TODO: don't know why must add limitation
+ addLock.wait(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ addDispatched = false;
+ }
+ return added;
+ }
+
+ public int getRemoved() {
+ synchronized (removeLock) {
+ if (!removeDispatched) {
+ try {
+ removeLock.wait(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ removeDispatched = false;
+ }
+ return removed;
+
+ }
+
+ public void reset() {
+ added = 0;
+ removed = 0;
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferenceChangeListener.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferenceChangeListener.java
new file mode 100644
index 0000000..b09383e
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferenceChangeListener.java
@@ -0,0 +1,102 @@
+package org.apache.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+
+public class MockPreferenceChangeListener implements PreferenceChangeListener {
+ private Object lock = new Object();
+
+ private int changed = 0;
+
+ private boolean addDispatched = false;
+
+ public static final int TEST_GET_KEY = 1;
+
+ public static final int TEST_GET_NEW_VALUE = 2;
+
+ public static final int TEST_GET_NODE = 3;
+
+ boolean result = false;
+
+ int testNum = 0;
+
+
+ public MockPreferenceChangeListener() {
+
+ }
+
+ public MockPreferenceChangeListener(int test) {
+ testNum = test;
+ }
+
+ // private Object lock = new Object();
+
+ public void preferenceChange(PreferenceChangeEvent pce) {
+ synchronized (lock) {
+ switch(testNum) {
+ case TEST_GET_KEY:
+ if(pce != null) {
+ if(pce.getKey().equals("key_int")) {
+ result = true;
+ }
+ }
+ break;
+ case TEST_GET_NEW_VALUE:
+ if(pce != null) {
+ if(pce.getNewValue().equals(new Integer(Integer.MAX_VALUE).toString())) {
+ result = true;
+ }
+ }
+ break;
+ case TEST_GET_NODE:
+ if(pce != null) {
+ if("mock".equals(pce.getNode().name())) {
+ result = true;
+ }
+ }
+
+ break;
+ }
+ changed++;
+ addDispatched = true;
+ lock.notifyAll();
+ }
+ }
+
+ public boolean getResult() {
+ synchronized (lock) {
+
+ if (!addDispatched) {
+ try {
+ // TODO: don't know why must add limitation
+ lock.wait(100);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ addDispatched = false;
+ return result;
+ }
+ }
+
+ public int getChanged() {
+ synchronized (lock) {
+
+ if (!addDispatched) {
+ try {
+ // TODO: don't know why must add limitation
+ lock.wait(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ addDispatched = false;
+ return changed;
+ }
+ }
+
+ public void reset() {
+ changed = 0;
+ result = false;
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferencesFactory.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferencesFactory.java
new file mode 100644
index 0000000..08b43ec
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferencesFactory.java
@@ -0,0 +1,42 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.Preferences;
+import java.util.prefs.PreferencesFactory;
+
+/**
+ *
+ */
+public class MockPreferencesFactory implements PreferencesFactory {
+ static MockAbstractPreferences userRoot = new MockAbstractPreferences(null,
+ "");
+
+ static MockAbstractPreferences systemRoot = new MockAbstractPreferences(
+ null, "");
+
+ public MockPreferencesFactory() {
+ }
+
+ public Preferences userRoot() {
+ return userRoot;
+ }
+
+ public Preferences systemRoot() {
+ return systemRoot;
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java
new file mode 100644
index 0000000..27f9a97
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java
@@ -0,0 +1,60 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.security.Permission;
+
+/**
+ * utility class for java.util.prefs test
+ *
+ */
+class MockSecurityManager extends SecurityManager {
+
+ SecurityManager dflt;
+
+ public MockSecurityManager() {
+ super();
+ dflt = System.getSecurityManager();
+ }
+
+ public void install() {
+ System.setSecurityManager(this);
+ }
+
+ public void restoreDefault() {
+ System.setSecurityManager(dflt);
+ }
+
+ public void checkPermission(Permission perm) {
+ if (perm instanceof RuntimePermission
+ && perm.getName().equals("preferences")) {
+ throw new SecurityException();
+ } else if (dflt != null) {
+ dflt.checkPermission(perm);
+ }
+ }
+
+ public void checkPermission(Permission perm, Object ctx) {
+ if (perm instanceof RuntimePermission
+ && perm.getName().equals("preferences")) {
+ throw new SecurityException();
+ } else if (dflt != null) {
+ dflt.checkPermission(perm, ctx);
+ }
+ }
+
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java
new file mode 100644
index 0000000..79835ba
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java
@@ -0,0 +1,115 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.io.NotSerializableException;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.NodeChangeEvent;
+import java.util.prefs.Preferences;
+
+import junit.framework.TestCase;
+
+import org.apache.harmony.testframework.serialization.SerializationTest;
+
+/**
+ *
+ */
+public class NodeChangeEventTest extends TestCase {
+
+ NodeChangeEvent event;
+
+ public void testConstructor() {
+ event = new NodeChangeEvent(Preferences.systemRoot(), Preferences
+ .userRoot());
+ assertSame(Preferences.systemRoot(), event.getParent());
+ assertSame(Preferences.userRoot(), event.getChild());
+ assertSame(Preferences.systemRoot(), event.getSource());
+ }
+
+ public void testConstructorNullParam() {
+ try {
+ event = new NodeChangeEvent(null, Preferences.userRoot());
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ event = new NodeChangeEvent(Preferences.systemRoot(), null);
+ assertSame(Preferences.systemRoot(), event.getParent());
+ assertNull(event.getChild());
+ assertSame(Preferences.systemRoot(), event.getSource());
+ }
+
+ public void testSerialization() throws Exception {
+
+ event = new NodeChangeEvent(Preferences.systemRoot(), null);
+
+ try {
+ SerializationTest.copySerializable(event);
+ fail("No expected NotSerializableException");
+ } catch (NotSerializableException e) {
+ }
+ }
+
+ public void testGetChild() throws BackingStoreException {
+
+ AbstractPreferences parent = (AbstractPreferences) Preferences
+ .userNodeForPackage(Preferences.class);
+
+ AbstractPreferences pref = (AbstractPreferences) parent.node("mock");
+
+ MockNodeChangeListener nl = new MockNodeChangeListener(
+ MockNodeChangeListener.TEST_GET_CHILD);
+ try {
+ pref.addNodeChangeListener(nl);
+ Preferences child1 = pref.node("mock1");
+ assertEquals(1, nl.getAdded());
+ assertTrue(nl.getAddResult());
+ nl.reset();
+ child1.removeNode();
+ assertEquals(1, nl.getRemoved());
+ assertTrue(nl.getRemoveResult());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ }
+ }
+
+ public void testGetParent() throws BackingStoreException {
+
+ AbstractPreferences parent = (AbstractPreferences) Preferences
+ .userNodeForPackage(Preferences.class);
+
+ AbstractPreferences pref = (AbstractPreferences) parent.node("mock");
+
+ MockNodeChangeListener nl = new MockNodeChangeListener(
+ MockNodeChangeListener.TEST_GET_CHILD);
+ try {
+ pref.addNodeChangeListener(nl);
+ Preferences child1 = pref.node("mock1");
+ assertEquals(1, nl.getAdded());
+ assertTrue(nl.getAddResult());
+ nl.reset();
+ child1.removeNode();
+ assertEquals(1, nl.getRemoved());
+ assertTrue(nl.getRemoveResult());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ }
+ }
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java
new file mode 100644
index 0000000..509fa9e
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java
@@ -0,0 +1,67 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.NodeChangeEvent;
+import java.util.prefs.NodeChangeListener;
+import java.util.prefs.Preferences;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ */
+public class NodeChangeListenerTest extends TestCase {
+
+ NodeChangeListener l;
+
+ /*
+ * @see TestCase#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ l = new NodeChangeListenerImpl();
+ }
+
+ /*
+ * @see TestCase#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testChildAdded() {
+ l.childAdded(new NodeChangeEvent(Preferences.userRoot(), Preferences
+ .userRoot()));
+ }
+
+ public void testChildRemoved() {
+ l.childRemoved(new NodeChangeEvent(Preferences.userRoot(), Preferences
+ .userRoot()));
+ }
+
+ public static class NodeChangeListenerImpl implements NodeChangeListener {
+
+ public void childAdded(NodeChangeEvent e) {
+ }
+
+ public void childRemoved(NodeChangeEvent e) {
+ }
+
+ }
+
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java
new file mode 100644
index 0000000..c3709dc
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java
@@ -0,0 +1,151 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.io.NotSerializableException;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.Preferences;
+
+import junit.framework.TestCase;
+
+import org.apache.harmony.testframework.serialization.SerializationTest;
+
+/**
+ *
+ */
+public class PreferenceChangeEventTest extends TestCase {
+
+ PreferenceChangeEvent event;
+
+ public void testPreferenceChangeEventException() {
+ try {
+ event = new PreferenceChangeEvent(null, "key", "value");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ public void testConstructorNullValue() {
+ event = new PreferenceChangeEvent(Preferences.userRoot(), "key", null);
+ assertEquals("key", event.getKey());
+ assertNull(event.getNewValue());
+ assertSame(Preferences.userRoot(), event.getNode());
+ assertSame(Preferences.userRoot(), event.getSource());
+
+ event = new PreferenceChangeEvent(Preferences.userRoot(), "", null);
+ assertEquals("", event.getKey());
+ assertNull(event.getNewValue());
+ assertSame(Preferences.userRoot(), event.getNode());
+ assertSame(Preferences.userRoot(), event.getSource());
+
+ event = new PreferenceChangeEvent(Preferences.userRoot(), null, "value");
+ assertNull(event.getKey());
+ assertEquals("value", event.getNewValue());
+ assertSame(Preferences.userRoot(), event.getNode());
+ assertSame(Preferences.userRoot(), event.getSource());
+
+ event = new PreferenceChangeEvent(Preferences.userRoot(), null, "");
+ assertNull(event.getKey());
+ assertEquals("", event.getNewValue());
+ assertSame(Preferences.userRoot(), event.getNode());
+ assertSame(Preferences.userRoot(), event.getSource());
+ }
+
+ public void testConstructor() {
+ event = new PreferenceChangeEvent(Preferences.userRoot(), "key",
+ "value");
+ assertEquals("key", event.getKey());
+ assertEquals("value", event.getNewValue());
+ assertSame(Preferences.userRoot(), event.getNode());
+ assertSame(Preferences.userRoot(), event.getSource());
+ }
+
+ public void testSerialization() throws Exception {
+ event = new PreferenceChangeEvent(Preferences.userRoot(), "key",
+ "value");
+ try {
+ SerializationTest.copySerializable(event);
+ fail("No expected NotSerializableException");
+ } catch (NotSerializableException e) {
+ }
+ }
+
+ public void testGetKey() {
+ AbstractPreferences parent = (AbstractPreferences) Preferences
+ .userNodeForPackage(Preferences.class);
+
+ AbstractPreferences pref = (AbstractPreferences) parent.node("mock");
+
+ MockPreferenceChangeListener pl = new MockPreferenceChangeListener(
+ MockPreferenceChangeListener.TEST_GET_KEY);
+ pref.addPreferenceChangeListener(pl);
+ try {
+ pref.putInt("key_int", Integer.MAX_VALUE);
+ assertEquals(1, pl.getChanged());
+ assertTrue(pl.getResult());
+ pl.reset();
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ }
+ }
+
+ public void testGetNewValue() {
+ AbstractPreferences parent = (AbstractPreferences) Preferences
+ .userNodeForPackage(Preferences.class);
+
+ AbstractPreferences pref = (AbstractPreferences) parent.node("mock");
+
+ MockPreferenceChangeListener pl = new MockPreferenceChangeListener(
+ MockPreferenceChangeListener.TEST_GET_NEW_VALUE);
+ pref.addPreferenceChangeListener(pl);
+ try {
+ pref.putInt("key_int", Integer.MAX_VALUE);
+ assertEquals(1, pl.getChanged());
+ assertTrue(pl.getResult());
+ pl.reset();
+
+ pref.putInt("key_int", Integer.MAX_VALUE);
+ assertEquals(1, pl.getChanged());
+ assertTrue(pl.getResult());
+ pl.reset();
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ }
+ }
+
+ public void testGetNode() {
+ AbstractPreferences parent = (AbstractPreferences) Preferences
+ .userNodeForPackage(Preferences.class);
+
+ AbstractPreferences pref = (AbstractPreferences) parent.node("mock");
+
+ MockPreferenceChangeListener pl = new MockPreferenceChangeListener(
+ MockPreferenceChangeListener.TEST_GET_NODE);
+ pref.addPreferenceChangeListener(pl);
+ try {
+ pref.putInt("key_int", Integer.MAX_VALUE);
+ assertEquals(1, pl.getChanged());
+ assertTrue(pl.getResult());
+ pl.reset();
+
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ }
+ }
+
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java
new file mode 100644
index 0000000..c417052
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java
@@ -0,0 +1,52 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.PreferenceChangeEvent;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ */
+public class PreferenceChangeListenerTest extends TestCase {
+
+ PreferenceChangeListener l;
+
+ /*
+ * @see TestCase#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ l = new PreferenceChangeListenerImpl();
+ }
+
+ public void testPreferenceChange() {
+ l.preferenceChange(new PreferenceChangeEvent(Preferences.userRoot(),
+ "", ""));
+ }
+
+ public static class PreferenceChangeListenerImpl implements
+ PreferenceChangeListener {
+ public void preferenceChange(PreferenceChangeEvent pce) {
+ }
+
+ }
+
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java
new file mode 100644
index 0000000..d0c3541
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java
@@ -0,0 +1,59 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.util.prefs.Preferences;
+import java.util.prefs.PreferencesFactory;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ */
+public class PreferencesFactoryTest extends TestCase {
+
+ PreferencesFactory f;
+
+ /*
+ * @see TestCase#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ f = new PreferencesFactoryImpl();
+ }
+
+ public void testUserRoot() {
+ f.userRoot();
+ }
+
+ public void testSystemRoot() {
+ f.systemRoot();
+ }
+
+ public static class PreferencesFactoryImpl implements PreferencesFactory {
+
+ public Preferences userRoot() {
+ return null;
+ }
+
+ public Preferences systemRoot() {
+ return null;
+ }
+
+ }
+
+}
diff --git a/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java
new file mode 100644
index 0000000..23248c0
--- /dev/null
+++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java
@@ -0,0 +1,1453 @@
+/* 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.harmony.prefs.tests.java.util.prefs;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.MalformedURLException;
+import java.util.Arrays;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.BackingStoreException;
+import java.util.prefs.InvalidPreferencesFormatException;
+import java.util.prefs.NodeChangeListener;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+
+import junit.framework.TestCase;
+
+/**
+ *
+ */
+public class PreferencesTest extends TestCase {
+
+ MockSecurityManager manager = new MockSecurityManager();
+
+ MockInputStream stream = null;
+
+ final static String longKey;
+
+ final static String longValue;
+
+ InputStream in;
+ static {
+ StringBuffer key = new StringBuffer(Preferences.MAX_KEY_LENGTH);
+ for (int i = 0; i < Preferences.MAX_KEY_LENGTH; i++) {
+ key.append('a');
+ }
+ longKey = key.toString();
+
+ StringBuffer value = new StringBuffer(Preferences.MAX_VALUE_LENGTH);
+ for (int i = 0; i < Preferences.MAX_VALUE_LENGTH; i++) {
+ value.append('a');
+ }
+ longValue = value.toString();
+ }
+
+ /*
+ * @see TestCase#setUp()
+ */
+ protected void setUp() throws Exception {
+ super.setUp();
+ in = new ByteArrayInputStream(
+ "<!DOCTYPE preferences SYSTEM \"http://java.sun.com/dtd/preferences.dtd\"><preferences><root type=\"user\"><map></map></root></preferences>"
+ .getBytes("UTF-8"));
+ stream = new MockInputStream(in);
+ }
+
+ /*
+ * @see TestCase#tearDown()
+ */
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ stream.close();
+ }
+
+ public void testSystemNodeForPackage() throws BackingStoreException {
+ Preferences p = null;
+ try {
+ p = Preferences.systemNodeForPackage(Object.class);
+ } catch (SecurityException e) {
+ // may be caused by absence of privileges on the underlying OS
+ return;
+ }
+ assertEquals("/java/lang", p.absolutePath());
+ assertTrue(p instanceof AbstractPreferences);
+ Preferences root = Preferences.systemRoot();
+ Preferences parent = root.node("java");
+ assertSame(parent, p.parent());
+ assertFalse(p.isUserNode());
+ assertEquals("lang", p.name());
+ assertEquals("System Preference Node: " + p.absolutePath(), p
+ .toString());
+ try {
+ assertEquals(0, p.childrenNames().length);
+ } catch (BackingStoreException e) {
+ // could be thrown according to specification
+ }
+ try {
+ assertEquals(0, p.keys().length);
+ } catch (BackingStoreException e) {
+ // could be thrown according to specification
+ }
+
+ try {
+ p = Preferences.userNodeForPackage(null);
+ fail("NullPointerException has not been thrown");
+ } catch (NullPointerException e) {
+ // expected
+ }
+ }
+
+ public void testSystemRoot() throws BackingStoreException {
+ Preferences p = Preferences.systemRoot();
+ assertTrue(p instanceof AbstractPreferences);
+ assertEquals("/", p.absolutePath());
+ assertSame(null, p.parent());
+ assertFalse(p.isUserNode());
+ assertEquals("", p.name());
+ assertEquals("System Preference Node: " + p.absolutePath(), p
+ .toString());
+ // assertEquals(0, p.childrenNames().length);
+ // assertEquals(0, p.keys().length);
+ }
+
+ public void testConsts() {
+ assertEquals(80, Preferences.MAX_KEY_LENGTH);
+ assertEquals(80, Preferences.MAX_NAME_LENGTH);
+ assertEquals(8192, Preferences.MAX_VALUE_LENGTH);
+ }
+
+ public void testUserNodeForPackage() throws BackingStoreException {
+ Preferences p = Preferences.userNodeForPackage(Object.class);
+ assertEquals("/java/lang", p.absolutePath());
+ assertTrue(p instanceof AbstractPreferences);
+ Preferences root = Preferences.userRoot();
+ Preferences parent = root.node("java");
+ assertSame(parent, p.parent());
+ assertTrue(p.isUserNode());
+ assertEquals("lang", p.name());
+ assertEquals("User Preference Node: " + p.absolutePath(), p.toString());
+ assertEquals(0, p.childrenNames().length);
+ assertEquals(0, p.keys().length);
+
+ try {
+ p = Preferences.userNodeForPackage(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ }
+
+ public void testUserRoot() throws BackingStoreException {
+ Preferences p = Preferences.userRoot();
+ assertTrue(p instanceof AbstractPreferences);
+ assertEquals("/", p.absolutePath());
+ assertSame(null, p.parent());
+ assertTrue(p.isUserNode());
+ assertEquals("", p.name());
+ assertEquals("User Preference Node: " + p.absolutePath(), p.toString());
+ // assertEquals(0, p.childrenNames().length);
+ // assertEquals(p.keys().length, 0);
+ }
+
+ public void testImportPreferences() throws Exception {
+ Preferences prefs = null;
+ try {
+ prefs = Preferences.userNodeForPackage(PreferencesTest.class);
+ // assertEquals(0, prefs.childrenNames().length);
+ // assertFalse(prefs.nodeExists("mock/child/grandson"));
+
+ prefs.put("prefskey", "oldvalue");
+ prefs.put("prefskey2", "oldvalue2");
+ in = getClass().getResourceAsStream("/prefs/java/util/prefs/userprefs.xml");
+ Preferences.importPreferences(in);
+
+ prefs = Preferences.userNodeForPackage(PreferencesTest.class);
+ assertEquals(1, prefs.childrenNames().length);
+ assertTrue(prefs.nodeExists("mock/child/grandson"));
+ assertEquals("newvalue", prefs.get("prefskey", null));
+ assertEquals("oldvalue2", prefs.get("prefskey2", null));
+ assertEquals("newvalue3", prefs.get("prefskey3", null));
+
+ in = PreferencesTest.class
+ .getResourceAsStream("/prefs/java/util/prefs/userprefs-badform.xml");
+ try {
+ Preferences.importPreferences(in);
+ fail();
+ } catch (InvalidPreferencesFormatException e) {
+ }
+
+ in = PreferencesTest.class
+ .getResourceAsStream("/prefs/java/util/prefs/userprefs-badtype.xml");
+ try {
+ Preferences.importPreferences(in);
+ fail();
+ } catch (InvalidPreferencesFormatException e) {
+ }
+
+ // in = PreferencesTest.class
+ // .getResourceAsStream("/prefs/java/util/prefs/userprefs-badencoding.xml");
+ // try {
+ // Preferences.importPreferences(in);
+ // fail();
+ // } catch (InvalidPreferencesFormatException e) {
+ // }
+
+ in = PreferencesTest.class
+ .getResourceAsStream("/prefs/java/util/prefs/userprefs-higherversion.xml");
+ try {
+ Preferences.importPreferences(in);
+ fail();
+ } catch (InvalidPreferencesFormatException e) {
+ }
+
+ in = PreferencesTest.class
+ .getResourceAsStream("/prefs/java/util/prefs/userprefs-ascii.xml");
+ Preferences.importPreferences(in);
+ prefs = Preferences.userNodeForPackage(PreferencesTest.class);
+ } finally {
+ try {
+ prefs = Preferences.userRoot().node("tests");
+ prefs.removeNode();
+ } catch (Exception e) {
+ }
+ }
+ }
+
+ public void testImportPreferencesException() throws Exception {
+ try {
+ Preferences.importPreferences(null);
+ fail();
+ } catch (MalformedURLException e) {
+ }
+
+ byte[] source = new byte[0];
+ InputStream in = new ByteArrayInputStream(source);
+ try {
+ Preferences.importPreferences(in);
+ fail();
+ } catch (InvalidPreferencesFormatException e) {
+ }
+
+ stream.setResult(MockInputStream.exception);
+ try {
+ Preferences.importPreferences(stream);
+ fail();
+ } catch (IOException e) {
+ }
+
+ stream.setResult(MockInputStream.runtimeException);
+ try {
+ Preferences.importPreferences(stream);
+ fail();
+ } catch (RuntimeException e) {
+ }
+ }
+
+ public void testSecurity() throws InvalidPreferencesFormatException,
+ IOException {
+ try {
+ manager.install();
+ try {
+ Preferences.userRoot();
+ fail();
+ } catch (SecurityException e) {
+ }
+ try {
+ Preferences.systemRoot();
+ fail();
+ } catch (SecurityException e) {
+ }
+ try {
+ Preferences.userNodeForPackage(null);
+ fail();
+ } catch (SecurityException e) {
+ }
+
+ try {
+ Preferences.systemNodeForPackage(null);
+ fail();
+ } catch (SecurityException e) {
+ }
+
+ try {
+ Preferences.importPreferences(stream);
+ fail();
+ } catch (SecurityException e) {
+ }
+ } finally {
+ manager.restoreDefault();
+ }
+ }
+
+ public void testAbstractMethods() {
+ Preferences p = new MockPreferences();
+ p.absolutePath();
+ try {
+ p.childrenNames();
+ } catch (BackingStoreException e4) {
+ }
+ try {
+ p.clear();
+ } catch (BackingStoreException e5) {
+ }
+ try {
+ p.exportNode(null);
+ } catch (IOException e6) {
+ } catch (BackingStoreException e6) {
+ }
+ try {
+ p.exportSubtree(null);
+ } catch (IOException e7) {
+ } catch (BackingStoreException e7) {
+ }
+ try {
+ p.flush();
+ } catch (BackingStoreException e8) {
+ }
+ p.get(null, null);
+ p.getBoolean(null, false);
+ p.getByteArray(null, null);
+ p.getFloat(null, 0.1f);
+ p.getDouble(null, 0.1);
+ p.getInt(null, 1);
+ p.getLong(null, 1l);
+ p.isUserNode();
+ try {
+ p.keys();
+ } catch (BackingStoreException e) {
+ }
+ p.name();
+ p.node(null);
+ try {
+ p.nodeExists(null);
+ } catch (BackingStoreException e1) {
+ }
+ p.parent();
+ p.put(null, null);
+ p.putBoolean(null, false);
+ p.putByteArray(null, null);
+ p.putDouble(null, 1);
+ p.putFloat(null, 1f);
+ p.putInt(null, 1);
+ p.putLong(null, 1l);
+ p.remove(null);
+ try {
+ p.removeNode();
+ } catch (BackingStoreException e2) {
+ }
+ p.addNodeChangeListener(null);
+ p.addPreferenceChangeListener(null);
+ p.removeNodeChangeListener(null);
+ p.removePreferenceChangeListener(null);
+ try {
+ p.sync();
+ } catch (BackingStoreException e3) {
+ }
+ p.toString();
+ }
+
+ public void testConstructor() {
+ MockPreferences mp = new MockPreferences();
+ assertEquals(mp.getClass(), MockPreferences.class);
+ }
+
+ public void testToString() {
+ Preferences p1 = Preferences.userNodeForPackage(Preferences.class);
+ assertNotNull(p1.toString());
+
+ Preferences p2 = Preferences.systemRoot();
+ assertNotNull(p2.toString());
+
+ Preferences p3 = Preferences.userRoot();
+ assertNotNull(p3.toString());
+ }
+ /**
+ * @test java.util.prefs.Preferences#absolutePath()
+ *
+ */
+ public void testAbsolutePath() {
+ Preferences p = Preferences.userNodeForPackage(Preferences.class);
+ assertEquals("/java/util/prefs", p.absolutePath());
+
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#childrenNames()
+ *
+ */
+ public void testChildrenNames() throws BackingStoreException {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+
+ Preferences child1 = pref.node("child1");
+
+ pref.node("child2");
+ pref.node("child3");
+ child1.node("subchild1");
+
+ assertSame(pref, child1.parent());
+ assertEquals(4, pref.childrenNames().length);
+ assertEquals("child1", pref.childrenNames()[0]);
+ assertEquals(1, child1.childrenNames().length);
+ assertEquals("subchild1", child1.childrenNames()[0]);
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#clear()
+ *
+ */
+ public void testClear() throws BackingStoreException {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ pref.put("testClearKey", "testClearValue");
+ pref.put("testClearKey1", "testClearValue1");
+ assertEquals("testClearValue", pref.get("testClearKey", null));
+ assertEquals("testClearValue1", pref.get("testClearKey1", null));
+ pref.clear();
+ assertNull(pref.get("testClearKey", null));
+ assertNull(pref.get("testClearKey1", null));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#get(String key, String def)
+ *
+ */
+ public void testGet() throws BackingStoreException {
+ Preferences root = Preferences.userNodeForPackage(Preferences.class);
+ Preferences pref = root.node("mock");
+ assertNull(pref.get("", null));
+ assertEquals("default", pref.get("key", "default"));
+ assertNull(pref.get("key", null));
+ pref.put("testGetkey", "value");
+ assertNull(pref.get("testGetKey", null));
+ assertEquals("value", pref.get("testGetkey", null));
+
+ try {
+ pref.get(null, "abc");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.get("", "abc");
+ pref.get("key", null);
+ pref.get("key", "");
+ pref.putFloat("floatKey", 1.0f);
+ assertEquals("1.0", pref.get("floatKey", null));
+
+ pref.removeNode();
+ try {
+ pref.get("key", "abc");
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ try {
+ pref.get(null, "abc");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#getBoolean(String key, boolean def)
+ *
+ */
+ public void testGetBoolean() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.getBoolean(null, false);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetBooleanKey", "false");
+ pref.put("testGetBooleanKey2", "value");
+ assertFalse(pref.getBoolean("testGetBooleanKey", true));
+ assertTrue(pref.getBoolean("testGetBooleanKey2", true));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#getByteArray(String key, byte[] def)
+ *
+ */
+ public void testGetByteArray() throws UnsupportedEncodingException {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.getByteArray(null, new byte[0]);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ byte[] b64Array = new byte[] { 0x59, 0x57, 0x4a, 0x6a };// BASE64
+
+ pref.put("testGetByteArrayKey", "abc=");
+ pref.put("testGetByteArrayKey2", new String(b64Array));
+ pref.put("invalidKey", "<>?");
+ assertTrue(Arrays.equals(new byte[] { 105, -73 }, pref.getByteArray(
+ "testGetByteArrayKey", new byte[0])));
+ assertTrue(Arrays.equals(new byte[] { 'a', 'b', 'c' }, pref
+ .getByteArray("testGetByteArrayKey2", new byte[0])));
+ assertTrue(Arrays.equals(new byte[0], pref.getByteArray("invalidKey",
+ new byte[0])));
+
+ pref.putByteArray("testGetByteArrayKey3", b64Array);
+ pref.putByteArray("testGetByteArrayKey4", "abc".getBytes());
+ assertTrue(Arrays.equals(b64Array, pref.getByteArray(
+ "testGetByteArrayKey3", new byte[0])));
+ assertTrue(Arrays.equals("abc".getBytes(), pref.getByteArray(
+ "testGetByteArrayKey4", new byte[0])));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#getDouble(String key, double def)
+ *
+ */
+ public void testGetDouble() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.getDouble(null, 0);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetDoubleKey", "1");
+ pref.put("testGetDoubleKey2", "value");
+ pref.putDouble("testGetDoubleKey3", 1);
+ pref.putInt("testGetDoubleKey4", 1);
+ assertEquals(1.0, pref.getDouble("testGetDoubleKey", 0.0), 0);
+ assertEquals(0.0, pref.getDouble("testGetDoubleKey2", 0.0), 0);
+ assertEquals(1.0, pref.getDouble("testGetDoubleKey3", 0.0), 0);
+ assertEquals(1.0, pref.getDouble("testGetDoubleKey4", 0.0), 0);
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#getFloat(String key, float def)
+ *
+ */
+ public void testGetFloat() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.getFloat(null, 0f);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.put("testGetFloatKey", "1");
+ pref.put("testGetFloatKey2", "value");
+ assertEquals(1f, pref.getFloat("testGetFloatKey", 0f), 0); //$NON-NLS-1$
+ assertEquals(0f, pref.getFloat("testGetFloatKey2", 0f), 0);
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#getInt(String key, int def)
+ *
+ */
+ public void testGetInt() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.getInt(null, 0);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetIntKey", "1");
+ pref.put("testGetIntKey2", "value");
+ assertEquals(1, pref.getInt("testGetIntKey", 0));
+ assertEquals(0, pref.getInt("testGetIntKey2", 0));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#getLong(String key, long def)
+ *
+ */
+ public void testGetLong() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.getLong(null, 0);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.put("testGetLongKey", "1");
+ pref.put("testGetLongKey2", "value");
+ assertEquals(1, pref.getInt("testGetLongKey", 0));
+ assertEquals(0, pref.getInt("testGetLongKey2", 0));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#isUserNode()
+ *
+ */
+ public void testIsUserNode() {
+ Preferences pref1 = Preferences.userNodeForPackage(Preferences.class);
+ assertTrue(pref1.isUserNode());
+
+ Preferences pref2 = Preferences.systemNodeForPackage(Preferences.class);
+ assertFalse(pref2.isUserNode());
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#keys()
+ *
+ */
+ public void testKeys() throws BackingStoreException {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ pref.clear();
+
+ pref.put("key0", "value");
+ pref.put("key1", "value1");
+ pref.put("key2", "value2");
+ pref.put("key3", "value3");
+
+ String[] keys = pref.keys();
+ assertEquals(4, keys.length);
+ for (int i = 0; i < keys.length; i++) {
+ assertEquals(0, keys[i].indexOf("key"));
+ assertEquals(4, keys[i].length());
+ }
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#name()
+ *
+ */
+ public void testName() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ Preferences child = pref.node("mock");
+ assertEquals("mock", child.name());
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#node(String pathName)
+ *
+ */
+ public void testNode() throws BackingStoreException {
+ StringBuffer name = new StringBuffer(Preferences.MAX_NAME_LENGTH);
+ for (int i = 0; i < Preferences.MAX_NAME_LENGTH; i++) {
+ name.append('a');
+ }
+ String longName = name.toString();
+
+ Preferences root = Preferences.userRoot();
+ Preferences parent = Preferences
+ .userNodeForPackage(Preferences.class);
+ Preferences pref = parent.node("mock");
+
+ try {
+ pref.node(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.node("/java/util/prefs/");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.node("/java//util/prefs");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.node(longName + "a");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ assertNotNull(pref.node(longName));
+
+ assertSame(root, pref.node("/"));
+
+ Preferences prefs = pref.node("/java/util/prefs");
+ assertSame(prefs, parent);
+
+ assertSame(pref, pref.node(""));
+
+ if (!(pref instanceof MockAbstractPreferences)) {
+ return;
+ }
+ MockAbstractPreferences child = (MockAbstractPreferences) ((MockAbstractPreferences) pref)
+ .publicChildSpi("child");
+ assertSame(child, pref.node("child"));
+
+ Preferences child2 = pref.node("child2");
+ assertSame(child2, ((MockAbstractPreferences) pref)
+ .publicChildSpi("child2"));
+
+ Preferences grandchild = pref.node("child/grandchild");
+ assertSame(grandchild, child.childSpi("grandchild"));
+ assertSame(grandchild, child.cachedChildrenImpl()[0]);
+ grandchild.removeNode();
+ assertNotSame(grandchild, pref.node("child/grandchild"));
+
+ grandchild = pref.node("child3/grandchild");
+ AbstractPreferences[] childs = ((MockAbstractPreferences) pref)
+ .cachedChildrenImpl();
+ Preferences child3 = child;
+ for (int i = 0; i < childs.length; i++) {
+ if (childs[i].name().equals("child3")) {
+ child3 = childs[i];
+ break;
+ }
+ }
+ assertSame(child3, grandchild.parent());
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#nodeExists(String pathName)
+ *
+ */
+ public void testNodeExists() throws BackingStoreException {
+
+ Preferences parent = Preferences
+ .userNodeForPackage(Preferences.class);
+ Preferences pref = parent.node("mock");
+
+ try {
+ pref.nodeExists(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.nodeExists("/java/util/prefs/");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.nodeExists("/java//util/prefs");
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ assertTrue(pref.nodeExists("/"));
+
+ assertTrue(pref.nodeExists("/java/util/prefs"));
+
+ assertTrue(pref.nodeExists(""));
+
+ assertFalse(pref.nodeExists("child"));
+ Preferences grandchild = pref.node("child/grandchild");
+ assertTrue(pref.nodeExists("child"));
+ assertTrue(pref.nodeExists("child/grandchild"));
+ grandchild.removeNode();
+ assertTrue(pref.nodeExists("child"));
+ assertFalse(pref.nodeExists("child/grandchild"));
+ assertFalse(grandchild.nodeExists(""));
+
+ assertFalse(pref.nodeExists("child2/grandchild"));
+ pref.node("child2/grandchild");
+ assertTrue(pref.nodeExists("child2/grandchild"));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#parent()
+ *
+ */
+ public void testParent() {
+ Preferences parent = Preferences
+ .userNodeForPackage(Preferences.class);
+ Preferences pref = parent.node("mock");
+
+ assertSame(parent, pref.parent());
+
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#put(String key, String value)
+ *
+ */
+ public void testPut() throws BackingStoreException {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ pref.put("", "emptyvalue");
+ assertEquals("emptyvalue", pref.get("", null));
+ pref.put("testPutkey", "value1");
+ assertEquals("value1", pref.get("testPutkey", null));
+ pref.put("testPutkey", "value2");
+ assertEquals("value2", pref.get("testPutkey", null));
+
+ pref.put("", "emptyvalue");
+ assertEquals("emptyvalue", pref.get("", null));
+
+ try {
+ pref.put(null, "value");
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.put("key", null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.put(longKey, longValue);
+ try {
+ pref.put(longKey + 1, longValue);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.put(longKey, longValue + 1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ pref.removeNode();
+ try {
+ pref.put(longKey, longValue + 1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ try {
+ pref.put(longKey, longValue);
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#putBoolean(String key, boolean value)
+ *
+ */
+ public void testPutBoolean() {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ try {
+ pref.putBoolean(null, false);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putBoolean(longKey, false);
+ try {
+ pref.putBoolean(longKey + "a", false);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putBoolean("testPutBooleanKey", false);
+ assertEquals("false", pref.get("testPutBooleanKey", null));
+ assertFalse(pref.getBoolean("testPutBooleanKey", true));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#putDouble(String key, double value)
+ *
+ */
+ public void testPutDouble() {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ try {
+ pref.putDouble(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putDouble(longKey, 3);
+ try {
+ pref.putDouble(longKey + "a", 3);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putDouble("testPutDoubleKey", 3);
+ assertEquals("3.0", pref.get("testPutDoubleKey", null));
+ assertEquals(3, pref.getDouble("testPutDoubleKey", 0), 0);
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#putFloat(String key, float value)
+ *
+ */
+ public void testPutFloat() {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ try {
+ pref.putFloat(null, 3f);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putFloat(longKey, 3f);
+ try {
+ pref.putFloat(longKey + "a", 3f);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putFloat("testPutFloatKey", 3f);
+ assertEquals("3.0", pref.get("testPutFloatKey", null));
+ assertEquals(3f, pref.getFloat("testPutFloatKey", 0), 0);
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#putInt(String key, int value)
+ *
+ */
+ public void testPutInt() {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ try {
+ pref.putInt(null, 3);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putInt(longKey, 3);
+ try {
+ pref.putInt(longKey + "a", 3);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putInt("testPutIntKey", 3);
+ assertEquals("3", pref.get("testPutIntKey", null));
+ assertEquals(3, pref.getInt("testPutIntKey", 0));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#putLong(String key, long value)
+ *
+ */
+ public void testPutLong() {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ try {
+ pref.putLong(null, 3L);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ pref.putLong(longKey, 3L);
+ try {
+ pref.putLong(longKey + "a", 3L);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.putLong("testPutLongKey", 3L);
+ assertEquals("3", pref.get("testPutLongKey", null));
+ assertEquals(3L, pref.getLong("testPutLongKey", 0));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#putByteArray(String key, byte[] value)
+ *
+ */
+ public void testPutByteArray() {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ try {
+ pref.putByteArray(null, new byte[0]);
+ fail();
+ } catch (NullPointerException e) {
+ }
+ try {
+ pref.putByteArray("testPutByteArrayKey4", null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ pref.putByteArray(longKey, new byte[0]);
+ try {
+ pref.putByteArray(longKey + "a", new byte[0]);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ byte[] longArray = new byte[(int) (Preferences.MAX_VALUE_LENGTH * 0.74)];
+ byte[] longerArray = new byte[(int) (Preferences.MAX_VALUE_LENGTH * 0.75) + 1];
+ pref.putByteArray(longKey, longArray);
+ try {
+ pref.putByteArray(longKey, longerArray);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ pref.putByteArray("testPutByteArrayKey", new byte[0]);
+ assertEquals("", pref.get("testPutByteArrayKey", null));
+ assertTrue(Arrays.equals(new byte[0], pref.getByteArray(
+ "testPutByteArrayKey", null)));
+
+ pref.putByteArray("testPutByteArrayKey3", new byte[] { 'a', 'b', 'c' });
+ assertEquals("YWJj", pref.get("testPutByteArrayKey3", null));
+ assertTrue(Arrays.equals(new byte[] { 'a', 'b', 'c' }, pref
+ .getByteArray("testPutByteArrayKey3", null)));
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#remove(String key)
+ *
+ */
+ public void testRemove() throws BackingStoreException {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ pref.remove("key");
+
+ pref.put("key", "value");
+ assertEquals("value", pref.get("key", null));
+ pref.remove("key");
+ assertNull(pref.get("key", null));
+
+ pref.remove("key");
+
+ try {
+ pref.remove(null);
+ } catch (NullPointerException e) {
+ }
+
+ pref.removeNode();
+ try {
+ pref.remove("key");
+ fail();
+ } catch (IllegalStateException e) {
+ }
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#removeNode()
+ *
+ */
+ public void testRemoveNode() throws BackingStoreException {
+ Preferences pref = Preferences
+ .userNodeForPackage(Preferences.class);
+ Preferences child = pref.node("child");
+ Preferences child1 = pref.node("child1");
+ Preferences grandchild = child.node("grandchild");
+
+ pref.removeNode();
+
+ assertFalse(child.nodeExists(""));
+ assertFalse(child1.nodeExists(""));
+ assertFalse(grandchild.nodeExists(""));
+ assertFalse(pref.nodeExists(""));
+ }
+
+
+ /**
+ * @test java.util.prefs.Preferences#addNodeChangeListener(NodeChangeListener ncl)
+ *
+ */
+ public void testAddNodeChangeListener() throws BackingStoreException {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.addNodeChangeListener(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ Preferences child1 = null;
+ Preferences child2 = null;
+ Preferences child3 = null;
+
+ MockNodeChangeListener nl = null;
+ // To get existed node doesn't create the change event
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock1");
+ assertEquals(1, nl.getAdded());
+ nl.reset();
+ child2 = pref.node("mock1");
+ assertEquals(0, nl.getAdded());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ child1.removeNode();
+ }
+ // same listener can be added twice, and must be removed twice
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock2");
+ assertEquals(2, nl.getAdded());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ pref.removeNodeChangeListener(nl);
+ child1.removeNode();
+ }
+ // test remove event
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock3");
+ child1.removeNode();
+ assertEquals(1, nl.getRemoved());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ }
+ // test remove event with two listeners
+ try {
+ nl = new MockNodeChangeListener();
+ pref.addNodeChangeListener(nl);
+ pref.addNodeChangeListener(nl);
+ child1 = pref.node("mock6");
+ child1.removeNode();
+ assertEquals(2, nl.getRemoved());
+ nl.reset();
+ } finally {
+ pref.removeNodeChangeListener(nl);
+ pref.removeNodeChangeListener(nl);
+ }
+ // test add/remove indirect children, or remove several children at the
+ // same time
+ try {
+ nl = new MockNodeChangeListener();
+ child1 = pref.node("mock4");
+ child1.addNodeChangeListener(nl);
+ child2 = pref.node("mock4/mock5");
+ assertEquals(1, nl.getAdded());
+ nl.reset();
+ child3 = pref.node("mock4/mock5/mock6");
+ assertEquals(0, nl.getAdded());
+ nl.reset();
+
+ child3.removeNode();
+ assertEquals(0, nl.getRemoved());
+ nl.reset();
+
+ child3 = pref.node("mock4/mock7");
+ assertEquals(1, nl.getAdded());
+ nl.reset();
+
+ child1.removeNode();
+ assertEquals(2, nl.getRemoved());
+ nl.reset();
+ } finally {
+ try {
+ child1.removeNode();
+ } catch (Exception e) {
+ }
+ }
+
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#addPreferenceChangeListener(PreferenceChangeListener pcl)
+ *
+ */
+ public void testAddPreferenceChangeListener() {
+
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ MockPreferenceChangeListener pl = null;
+
+ // TODO: start from here
+ try {
+ pref.addPreferenceChangeListener(null);
+ fail();
+ } catch (NullPointerException e) {
+ }
+
+ // To get existed node doesn't create the change event
+ try {
+ pl = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(pl);
+ pref.putInt("mock1", 123);
+ assertEquals(1, pl.getChanged());
+ pref.putLong("long_key", Long.MAX_VALUE);
+ assertEquals(2, pl.getChanged());
+ pl.reset();
+ try {
+ pref.clear();
+ assertEquals(2, pl.getChanged());
+ } catch(BackingStoreException bse) {
+ pl.reset();
+ fail("BackingStoreException is thrown");
+ }
+ pl.reset();
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ //child1.removeNode();
+ }
+
+ // same listener can be added twice, and must be removed twice
+ try {
+ pl = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(pl);
+ pref.addPreferenceChangeListener(pl);
+ pref.putFloat("float_key", Float.MIN_VALUE);
+ assertEquals(2, pl.getChanged());
+ pl.reset();
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ pref.removePreferenceChangeListener(pl);
+
+ }
+ // test remove event
+ try {
+ pl = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(pl);
+ pref.putDouble("double_key", Double.MAX_VALUE);
+ assertEquals(1, pl.getChanged());
+ try {
+ pref.clear();
+ assertEquals(3, pl.getChanged());
+ } catch(BackingStoreException bse) {
+ fail("BackingStoreException is thrown");
+ }
+ pl.reset();
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ }
+ // test remove event with two listeners
+ try {
+ pl = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(pl);
+ pref.addPreferenceChangeListener(pl);
+ pref.putByteArray("byte_array_key", new byte [] {1 ,2 , 3});
+ try {
+ pref.clear();
+ assertEquals(4, pl.getChanged());
+ } catch(BackingStoreException bse) {
+ fail("BackingStoreException is thrown");
+ }
+ pl.reset();
+ } finally {
+ pref.removePreferenceChangeListener(pl);
+ pref.removePreferenceChangeListener(pl);
+ }
+
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#removeNodeChangeListener(NodeChangeListener ncl)
+ *
+ */
+ public void testRemoveNodeChangeListener() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.removeNodeChangeListener(null);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ MockNodeChangeListener l1 = new MockNodeChangeListener();
+ MockNodeChangeListener l2 = new MockNodeChangeListener();
+ pref.addNodeChangeListener(l1);
+ pref.addNodeChangeListener(l1);
+
+ pref.removeNodeChangeListener(l1);
+ pref.removeNodeChangeListener(l1);
+ try {
+ pref.removeNodeChangeListener(l1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ try {
+ pref.removeNodeChangeListener(l2);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ }
+
+ /**
+ * @test java.util.prefs.Preferences#removePreferenceChangeListener(PreferenceChangeListener pcl)
+ *
+ */
+ public void testRemovePreferenceChangeListener() {
+ Preferences pref = Preferences.userNodeForPackage(Preferences.class);
+ try {
+ pref.removePreferenceChangeListener(null);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ MockPreferenceChangeListener l1 = new MockPreferenceChangeListener();
+ MockPreferenceChangeListener l2 = new MockPreferenceChangeListener();
+ pref.addPreferenceChangeListener(l1);
+ pref.addPreferenceChangeListener(l1);
+ try {
+ pref.removePreferenceChangeListener(l2);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+ pref.removePreferenceChangeListener(l1);
+ pref.removePreferenceChangeListener(l1);
+ try {
+ pref.removePreferenceChangeListener(l1);
+ fail();
+ } catch (IllegalArgumentException e) {
+ }
+
+ }
+
+ static class MockInputStream extends InputStream {
+
+ static final int normal = 0;
+
+ static final int exception = 1;
+
+ static final int runtimeException = 2;
+
+ int result = normal;
+
+ InputStream wrapper;
+
+ public void setResult(int i) {
+ result = i;
+ }
+
+ private void checkException() throws IOException {
+ switch (result) {
+ case normal:
+ return;
+ case exception:
+ throw new IOException("test");
+ case runtimeException:
+ throw new RuntimeException("test");
+ }
+ }
+
+ public MockInputStream(InputStream in) {
+ wrapper = in;
+ }
+
+ public int read() throws IOException {
+ checkException();
+ return wrapper.read();
+ }
+ }
+
+ static class MockPreferences extends Preferences {
+
+ public MockPreferences() {
+ super();
+ }
+
+ public String absolutePath() {
+ return null;
+ }
+
+ public String[] childrenNames() throws BackingStoreException {
+ return null;
+ }
+
+ public void clear() throws BackingStoreException {
+ }
+
+ public void exportNode(OutputStream ostream) throws IOException,
+ BackingStoreException {
+ }
+
+ public void exportSubtree(OutputStream ostream) throws IOException,
+ BackingStoreException {
+ }
+
+ public void flush() throws BackingStoreException {
+ }
+
+ public String get(String key, String deflt) {
+ return null;
+ }
+
+ public boolean getBoolean(String key, boolean deflt) {
+ return false;
+ }
+
+ public byte[] getByteArray(String key, byte[] deflt) {
+ return null;
+ }
+
+ public double getDouble(String key, double deflt) {
+ return 0;
+ }
+
+ public float getFloat(String key, float deflt) {
+ return 0;
+ }
+
+ public int getInt(String key, int deflt) {
+ return 0;
+ }
+
+ public long getLong(String key, long deflt) {
+ return 0;
+ }
+
+ public boolean isUserNode() {
+ return false;
+ }
+
+ public String[] keys() throws BackingStoreException {
+ return null;
+ }
+
+ public String name() {
+ return null;
+ }
+
+ public Preferences node(String name) {
+ return null;
+ }
+
+ public boolean nodeExists(String name) throws BackingStoreException {
+ return false;
+ }
+
+ public Preferences parent() {
+ return null;
+ }
+
+ public void put(String key, String value) {
+
+ }
+
+ public void putBoolean(String key, boolean value) {
+
+ }
+
+ public void putByteArray(String key, byte[] value) {
+
+ }
+
+ public void putDouble(String key, double value) {
+
+ }
+
+ public void putFloat(String key, float value) {
+
+ }
+
+ public void putInt(String key, int value) {
+
+ }
+
+ public void putLong(String key, long value) {
+
+ }
+
+ public void remove(String key) {
+
+ }
+
+ public void removeNode() throws BackingStoreException {
+
+ }
+
+ public void addNodeChangeListener(NodeChangeListener ncl) {
+
+ }
+
+ public void addPreferenceChangeListener(PreferenceChangeListener pcl) {
+
+ }
+
+ public void removeNodeChangeListener(NodeChangeListener ncl) {
+
+ }
+
+ public void removePreferenceChangeListener(PreferenceChangeListener pcl) {
+
+ }
+
+ public void sync() throws BackingStoreException {
+
+ }
+
+ public String toString() {
+ return null;
+ }
+
+ }
+
+}
diff --git a/prefs/src/test/java/tests/prefs/AllTests.java b/prefs/src/test/java/tests/prefs/AllTests.java
new file mode 100644
index 0000000..97679c9
--- /dev/null
+++ b/prefs/src/test/java/tests/prefs/AllTests.java
@@ -0,0 +1,38 @@
+/* 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 tests.prefs;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+/**
+ * Test suite that includes all tests for the Math project.
+ */
+public class AllTests {
+
+ public static void main(String[] args) {
+ junit.textui.TestRunner.run(AllTests.suite());
+ }
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite("All Prefs test suites");
+ // $JUnit-BEGIN$
+ suite.addTest(org.apache.harmony.prefs.tests.java.util.prefs.AllTests.suite());
+ // $JUnit-END$
+ return suite;
+ }
+}
diff --git a/prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd b/prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd
new file mode 100644
index 0000000..a116015
--- /dev/null
+++ b/prefs/src/test/resources/prefs/java/util/prefs/preferences.dtd
@@ -0,0 +1,56 @@
+<!--
+ 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.
+ -->
+
+<!-- DTD for a Preferences tree. -->
+
+<!-- The preferences element is at the root of an XML document
+ representing a Preferences tree. -->
+<!ELEMENT preferences (root)>
+
+<!-- The preferences element contains an optional version attribute,
+ which specifies version of DTD. -->
+<!ATTLIST preferences EXTERNAL_XML_VERSION CDATA "0.0" >
+
+<!-- The root element has a map representing the root's preferences
+ (if any), and one node for each child of the root (if any). -->
+<!ELEMENT root (map, node*) >
+
+<!-- Additionally, the root contains a type attribute, which
+ specifies whether it's the system or user root. -->
+<!ATTLIST root
+ type (system|user) #REQUIRED >
+
+<!-- Each node has a map representing its preferences (if any),
+ and one node for each child (if any). -->
+
+<!ELEMENT node (map, node*) >
+
+<!-- Additionally, each node has a name attribute -->
+<!ATTLIST node
+ name CDATA #REQUIRED >
+
+<!-- A map represents the preferences stored at a node (if any). -->
+<!ELEMENT map (entry*) >
+
+<!-- An entry represents a single preference, which is simply
+ a key-value pair. -->
+<!ELEMENT entry EMPTY >
+<!ATTLIST entry
+ key CDATA #REQUIRED
+ value CDATA #REQUIRED > \ No newline at end of file
diff --git a/prefs/src/test/resources/prefs/java/util/prefs/userprefs-ascii.xml b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-ascii.xml
new file mode 100644
index 0000000..9d066f1
--- /dev/null
+++ b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-ascii.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="ascii"?>
+
+<!--
+ 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.
+-->
+
+<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
+
+<preferences EXTERNAL_XML_VERSION="1.0">
+ <root type="user">
+ <map />
+ <node name="tests">
+ <map />
+ <node name="api">
+ <map />
+ <node name="java">
+ <map />
+ <node name="util">
+ <map />
+ <node name="prefs">
+ <map>
+ <entry key="prefskey3" value="newvalue3" />
+ </map>
+ <node name="mock">
+ <map />
+ <node name="child">
+ <map>
+ <entry key="key2" value="value2" />
+ </map>
+ <node name="grandson">
+ <map>
+ <entry key="key3" value="value3" />
+ </map>
+ <node name="grandgrandson">
+ <map>
+ <entry key="key4" value="value4" />
+ </map>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </root>
+</preferences>
diff --git a/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badencoding.xml b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badencoding.xml
new file mode 100644
index 0000000..4b99e04
--- /dev/null
+++ b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badencoding.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="badencoding"?>
+
+<!--
+ 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.
+-->
+
+<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
+
+<preferences EXTERNAL_XML_VERSION="1.0">
+ <root type="user">
+ <map />
+ <node name="tests">
+ <map />
+ <node name="api">
+ <map />
+ <node name="java">
+ <map />
+ <node name="util">
+ <map />
+ <node name="prefs">
+ <map>
+ <entry key="prefskey" value="newvalue" />
+ <entry key="prefskey3" value="newvalue3" />
+ </map>
+ <node name="mock">
+ <map />
+ <node name="child">
+ <map>
+ <entry key="key2" value="value2" />
+ </map>
+ <node name="grandson">
+ <map>
+ <entry key="key3" value="value3" />
+ </map>
+ <node name="grandgrandson">
+ <map>
+ <entry key="key4" value="value4" />
+ </map>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </root>
+</preferences>
diff --git a/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badform.xml b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badform.xml
new file mode 100644
index 0000000..2c231b9
--- /dev/null
+++ b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badform.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
+
+<preferences EXTERNAL_XML_VERSION="1.0">
+ <root type="user">
+ <map />
+ <node name="java">
+ <map />
+ <node name="util">
+ <map />
+ <node name="prefs">
+ <map />
+ <node name="mock">
+ <map />
+ <node name="child">
+ <map>
+ <entry key="key2" value="value2" />
+ </map>
+ <node name="grandson">
+ <map>
+ <entry key="key3" value="value3" />
+ </map>
+ <node name="grandgrandson">
+ <map>
+ <entry key="key4" value="value4" />
+ </map>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </root>
diff --git a/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badtype.xml b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badtype.xml
new file mode 100644
index 0000000..f974617
--- /dev/null
+++ b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-badtype.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<!DOCTYPE preferences SYSTEM 'preferences.dtd'>
+
+<preferences EXTERNAL_XML_VERSION="1.0">
+ <root type="user">
+ <map />
+ <node name="tests">
+ <map />
+ <node name="api">
+ <map />
+ <node name="java">
+ <map />
+ <node name="util">
+ <map />
+ <node name="prefs">
+ <map>
+ <entry key="prefskey" value="newvalue" />
+ <entry key="prefskey3" value="newvalue3" />
+ </map>
+ <node name="mock">
+ <map />
+ <node name="child">
+ <map>
+ <entry key="key2" value="value2" />
+ </map>
+ <node name="grandson">
+ <map>
+ <entry key="key3" value="value3" />
+ </map>
+ <node name="grandgrandson">
+ <map>
+ <entry key="key4" value="value4" />
+ </map>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </root>
+</preferences>
diff --git a/prefs/src/test/resources/prefs/java/util/prefs/userprefs-higherversion.xml b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-higherversion.xml
new file mode 100644
index 0000000..a7f6aa8
--- /dev/null
+++ b/prefs/src/test/resources/prefs/java/util/prefs/userprefs-higherversion.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
+
+<preferences EXTERNAL_XML_VERSION="1.1">
+ <root type="user">
+ <map />
+ <node name="tests">
+ <map />
+ <node name="api">
+ <map />
+ <node name="java">
+ <map />
+ <node name="util">
+ <map />
+ <node name="prefs">
+ <map>
+ <entry key="prefskey" value="newvalue" />
+ <entry key="prefskey3" value="newvalue3" />
+ </map>
+ <node name="mock">
+ <map />
+ <node name="child">
+ <map>
+ <entry key="key2" value="value2" />
+ </map>
+ <node name="grandson">
+ <map>
+ <entry key="key3" value="value3" />
+ </map>
+ <node name="grandgrandson">
+ <map>
+ <entry key="key4" value="value4" />
+ </map>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </node>
+ </root>
+</preferences>
diff --git a/prefs/src/test/resources/prefs/java/util/prefs/userprefs.xml b/prefs/src/test/resources/prefs/java/util/prefs/userprefs.xml
new file mode 100644
index 0000000..f901f5b
--- /dev/null
+++ b/prefs/src/test/resources/prefs/java/util/prefs/userprefs.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ 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.
+-->
+
+<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>
+
+<preferences EXTERNAL_XML_VERSION="1.0">
+ <root type="user">
+ <map />
+ <node name="org">
+ <map />
+ <node name="apache">
+ <map />
+ <node name="harmony">
+ <map />
+ <node name="prefs">
+ <map />
+ <node name="tests">
+ <map />
+ <node name="java">
+ <map />
+ <node name="util">
+ <map />
+
+ <node name="prefs">
+ <map>
+ <entry key="prefskey" value="newvalue" />
+ <entry key="prefskey3" value="newvalue3" />
+ </map>
+
+ <node name="mock">
+ <map />
+ <node name="child">
+ <map>
+ <entry key="key2" value="value2" />
+ </map>
+ <node name="grandson">
+ <map>
+ <entry key="key3" value="value3" />
+ </map>
+ <node name="grandgrandson">
+ <map>
+ <entry key="key4" value="value4" />
+ </map>
+ </node> <!--grandgrandson-->
+ </node> <!--grandson-->
+ </node> <!--child-->
+ </node> <!--mock-->
+
+ </node> <!--prefs-->
+ </node> <!--util-->
+ </node> <!--java-->
+ </node> <!--tests-->
+ </node> <!--prefs-->
+ </node> <!--harmony-->
+ </node> <!--apache-->
+ </node> <!--org-->
+ </root>
+</preferences>
diff --git a/prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.golden.ser b/prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.golden.ser
new file mode 100644
index 0000000..4d26113
--- /dev/null
+++ b/prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.golden.ser
Binary files differ
diff --git a/prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.golden.ser b/prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.golden.ser
new file mode 100644
index 0000000..a15948d
--- /dev/null
+++ b/prefs/src/test/resources/serialization/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.golden.ser
Binary files differ