diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-03-03 19:28:47 -0800 |
commit | adc854b798c1cfe3bfd4c27d68d5cee38ca617da (patch) | |
tree | 6aed8b4923ca428942cbaa7e848d50237a3d31e0 /prefs | |
parent | 1c0fed63c71ddb230f3b304aac12caffbedf2f21 (diff) | |
download | libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.zip libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.gz libcore-adc854b798c1cfe3bfd4c27d68d5cee38ca617da.tar.bz2 |
auto import from //depot/cupcake/@135843
Diffstat (limited to 'prefs')
43 files changed, 9245 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..711cc01 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/AbstractPreferences.java @@ -0,0 +1,974 @@ +/* + * 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 abstract class is a partial implementation of the abstract class + * Preferences, which can be used to simplify {@code Preferences} provider's + * implementation. This class defines nine abstract SPI methods, which must be + * implemented by a preference provider. + * + * @since Android 1.0 + */ +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. + * + * @since Android 1.0 + */ + 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. + * + * @since Android 1.0 + */ + 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 + * ----------------------------------------------------------- + */ + /** + * Constructs a new {@code AbstractPreferences} instance using the given parent node + * and node name. + * + * @param parent + * the parent node of the new node or {@code null} to indicate + * that the new node is a root node. + * @param name + * the name of the new node or an empty string to indicate that + * this node is called "root". + * @throws IllegalArgumentException + * if the name contains a slash character or is empty if {@code + * parent} is not {@code null}. + * @since Android 1.0 + */ + 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 + * ----------------------------------------------------------- + */ + /** + * Returns an array of all cached child nodes. + * + * @return the array of cached child nodes. + * @since Android 1.0 + */ + protected final AbstractPreferences[] cachedChildren() { + return cachedNode.values().toArray(new AbstractPreferences[cachedNode.size()]); + } + + /** + * Returns the child node with the specified name or {@code null} if it + * doesn't exist. Implementers can assume that the name supplied to this method + * will be a valid node name string (conforming to the node naming format) and + * will not correspond to a node that has been cached or removed. + * + * @param name + * the name of the desired child node. + * @return the child node with the given name or {@code null} if it doesn't + * exist. + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + 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; + } + + } + + /** + * Returns whether this node has been removed by invoking the method {@code + * removeNode()}. + * + * @return {@code true}, if this node has been removed, {@code false} + * otherwise. + * @since Android 1.0 + */ + protected boolean isRemoved() { + synchronized (lock) { + return isRemoved; + } + } + + /** + * Flushes changes of this node to the backing store. This method should + * only flush this node and should not include the descendant nodes. Any + * implementation that wants to provide functionality to flush all nodes + * at once should override the method {@link #flush() flush()}. + * + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + protected abstract void flushSpi() throws BackingStoreException; + + /** + * Returns the names of all of the child nodes of this node or an empty array if + * this node has no children. The names of cached children are not required to be + * returned. + * + * @return the names of this node's children. + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + protected abstract String[] childrenNamesSpi() throws BackingStoreException; + + /** + * Returns the child preference node with the given name, creating it + * if it does not exist. The caller of this method should ensure that the + * given name is valid and that this node has not been removed or cached. + * If the named node has just been removed, the implementation + * of this method must create a new one instead of reactivating the removed + * one. + * <p> + * The new creation is not required to be persisted immediately until the + * flush method will be invoked. + * </p> + * + * @param name + * the name of the child preference to be returned. + * @return the child preference node. + * @since Android 1.0 + */ + protected abstract AbstractPreferences childSpi(String name); + + + /** + * Puts the given key-value pair into this node. Caller of this method + * should ensure that both of the given values are valid and that this + * node has not been removed. + * + * @param name + * the given preference key. + * @param value + * the given preference value. + * @since Android 1.0 + */ + protected abstract void putSpi(String name, String value); + + /** + * Gets the preference value mapped to the given key. The caller of this method + * should ensure that the given key is valid and that this node has not been + * removed. This method should not throw any exceptions but if it does, the + * caller will ignore the exception, regarding it as a {@code null} return value. + * + * @param key + * the given key to be searched for. + * @return the preference value mapped to the given key. + * @since Android 1.0 + */ + protected abstract String getSpi(String key); + + + /** + * Returns an array of all preference keys of this node or an empty array if + * no preferences have been found. The caller of this method should ensure that + * this node has not been removed. + * + * @return the array of all preference keys. + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + protected abstract String[] keysSpi() throws BackingStoreException; + + /** + * Removes this node from the preference hierarchy tree. The caller of this + * method should ensure that this node has no child nodes, which means the + * method {@link Preferences#removeNode() Preferences.removeNode()} should + * invoke this method multiple-times in bottom-up pattern. The removal is + * not required to be persisted until after it is flushed. + * + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + protected abstract void removeNodeSpi() throws BackingStoreException; + + /** + * Removes the preference with the specified key. The caller of this method + * should ensure that the given key is valid and that this node has not been + * removed. + * + * @param key + * the key of the preference that is to be removed. + * @since Android 1.0 + */ + protected abstract void removeSpi(String key); + + /** + * Synchronizes this node with the backing store. This method should only + * synchronize this node and should not include the descendant nodes. An + * implementation that wants to provide functionality to synchronize all nodes at once should + * override the method {@link #sync() sync()}. + * + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + 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..e8a805c --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/BackingStoreException.java @@ -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. + */ + +package java.util.prefs; + + +/** + * An exception to indicate that an error was encountered while accessing the + * backing store. + * + * @since Android 1.0 + */ +public class BackingStoreException extends Exception { + + private static final long serialVersionUID = 859796500401108469L; + + /** + * Constructs a new {@code BackingStoreException} instance with a detailed exception + * message. + * + * @param s + * the detailed exception message. + * @since Android 1.0 + */ + public BackingStoreException (String s) { + super(s); + } + + /** + * Constructs a new {@code BackingStoreException} instance with a nested {@code Throwable}. + * + * @param t + * the nested {@code Throwable}. + * @since Android 1.0 + */ + 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..cc68e62 --- /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; + +/** + * The default implementation of <code>PreferencesFactory</code> for the Linux + * platform, using the file system as its back end. + * + * @since Android 1.0 + */ +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..cf85fa0 --- /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; + +/** + * The default implementation of <code>AbstractPreferences</code> for the Linux platform, + * using the file system as its back end. + * + * TODO some sync mechanism with backend, Performance - check file edit date + * + * @since Android 1.0 + */ +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..b31b3a1 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/InvalidPreferencesFormatException.java @@ -0,0 +1,71 @@ +/* + * 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 could + * not be validated against the appropriate document type (specified by + * in the {@code Preferences}). + * + * @since Android 1.0 + */ +public class InvalidPreferencesFormatException extends Exception { + + private static final long serialVersionUID = -791715184232119669L; + + /** + * Constructs a new {@code InvalidPreferencesFormatException} instance with a + * detailed exception message. + * + * @param s + * the detailed exception message. + * @since Android 1.0 + */ + public InvalidPreferencesFormatException (String s) { + super(s); + } + + /** + * Constructs a new {@code InvalidPreferencesFormatException} instance with a + * detailed exception message and a nested {@code Throwable}. + * + * @param s + * the detailed exception message. + * @param t + * the nested {@code Throwable}. + * @since Android 1.0 + */ + public InvalidPreferencesFormatException (String s, Throwable t) { + super(s,t); + } + + /** + * Constructs a new {@code InvalidPreferencesFormatException} instance with a nested + * {@code Throwable}. + * + * @param t + * the nested {@code Throwable}. + * @since Android 1.0 + */ + 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..e9824bc --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/NodeChangeEvent.java @@ -0,0 +1,99 @@ +/* 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 that one child of the preference node has + * been added or deleted. + * <p> + * Please note that the serialization functionality has not yet been implemented, so + * the serialization methods do nothing but throw a {@code NotSerializableException}. + * </p> + * + * @since Android 1.0 + */ +public class NodeChangeEvent extends EventObject implements Serializable { + + private static final long serialVersionUID = 8068949086596572957L; + + private final Preferences parent; + private final Preferences child; + + /** + * Constructs a new {@code NodeChangeEvent} instance. + * + * @param p + * the {@code Preferences} instance that fired this event; this object is + * considered as the event source. + * @param c + * the child {@code Preferences} instance that was added or deleted. + * @since Android 1.0 + */ + public NodeChangeEvent (Preferences p, Preferences c) { + super(p); + parent = p; + child = c; + } + + /** + * Gets the {@code Preferences} instance that fired this event. + * + * @return the {@code Preferences} instance that fired this event. + * @since Android 1.0 + */ + public Preferences getParent() { + return parent; + } + + /** + * Gets the child {@code Preferences} node that was added or removed. + * + * @return the added or removed child {@code Preferences} node. + * @since Android 1.0 + */ + 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..f16b206 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/NodeChangeListener.java @@ -0,0 +1,54 @@ +/* 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 preference node change events. + * The implementation of this interface can be installed by the {@code Preferences} instance. + * + * @see NodeChangeEvent + * + * @since Android 1.0 + */ +public interface NodeChangeListener extends EventListener { + + /** + * This method gets called whenever a child node is added to another node. + * + * @param e + * the node change event. + * @since Android 1.0 + */ + public void childAdded (NodeChangeEvent e); + + /** + * This method gets called whenever a child node is removed from another + * node. + * + * @param e + * the node change event. + * @since Android 1.0 + */ + 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..7edd030 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/NodeSet.java @@ -0,0 +1,38 @@ +package java.util.prefs; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.ArrayList; +import java.util.Iterator; + +/** + * + * @since Android 1.0 + */ +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..f0f0787 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/PreferenceChangeEvent.java @@ -0,0 +1,116 @@ +/* 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 that a preference has been added, deleted + * or updated. + * <p> + * Please note that the serialization functionality has not yet been implemented, so + * the serialization methods do nothing but throw a {@code NotSerializableException}. + * </p> + * + * @since Android 1.0 + */ +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} instance. + * + * @param p + * the {@code Preferences} instance that fired this event; this object is + * considered as the event's source. + * @param k + * the changed preference key. + * @param v + * the new value of the changed preference, this value can be + * {@code null}, which means the preference has been removed. + * @since Android 1.0 + */ + public PreferenceChangeEvent(Preferences p, String k, String v) { + super(p); + node = p; + key = k; + value = v; + } + + /** + * Gets the key of the changed preference. + * + * @return the changed preference's key. + * @since Android 1.0 + */ + public String getKey() { + return key; + } + + /** + * Gets the new value of the changed preference or {@code null} if the + * preference has been removed. + * + * @return the new value of the changed preference or null if the preference + * has been removed. + * @since Android 1.0 + */ + public String getNewValue() { + return value; + } + + /** + * Gets the {@code Preferences} instance that fired this event. + * + * @return the {@code Preferences} instance that fired this event. + * @since Android 1.0 + */ + 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..28bb763 --- /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 events. The implementation of + * this interface can be installed by the {@code Preferences} instance. + * + * @see PreferenceChangeEvent + * + * @since Android 1.0 + */ +public interface PreferenceChangeListener extends EventListener { + + /** + * This method gets invoked whenever a preference is added, deleted or + * updated. + * + * @param pce + * the event instance which describes the changed {@code Preferences} + * instance and the preference value. + * @since Android 1.0 + */ + 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..b7a0c70 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/Preferences.java @@ -0,0 +1,1043 @@ +/* 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; + +// BEGIN android-added +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.Enumeration; +// END android-added + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.security.AccessController; +import java.security.PrivilegedAction; + +import org.apache.harmony.prefs.internal.nls.Messages; + +/** + * An instance of the class {@code Preferences} represents one node in a preference tree, + * which provides a mechanism to store and access configuration data in a + * hierarchical way. Two hierarchy trees are maintained, one for system + * preferences shared by all users and the other for user preferences + * specific to the user. {@code Preferences} hierarchy trees and data are stored + * in an implementation-dependent back-end. + * <p> + * Every node has one name and one unique absolute path following the same + * notational conventions as directories in a file system. The root node's + * name is "", and other node name strings cannot contain the slash character + * and cannot be empty. The root node's absolute path is "/", and all other + * nodes' absolute paths are constructed in the standard way: <parent's absolute + * path> + "/" + <node's name>. Since the set of nodes forms a tree with + * the root node at its base, all absolute paths start with the slash character. + * Every node has one relative path to each of its ancestors. The relative path + * doesn't start with slash: it equals the node's absolute path with leading + * substring removed corresponding to the ancestor's absolute path and a slash. + * </p> + * <p> + * Modification to preferences data may be asynchronous, which means that + * preference update method calls may return immediately instead of blocking. + * The {@code flush()} and {@code sync()} methods force the back-end to + * synchronously perform all pending updates, but the implementation is + * permitted to perform the modifications on the underlying back-end data + * at any time between the moment the request is made and the moment the + * {@code flush()} or {@code sync()} method returns. + * Please note that if JVM exit normally, the implementation must assure all + * modifications are persisted implicitly. + * </p> + * <p> + * When invoking a method that retrieves preferences, the user must provide + * a default value. The default value is returned when the preferences cannot + * be found or the back-end is unavailable. Some other methods will throw + * {@code BackingStoreException} when the back-end is unavailable. + * </p> + * <p> + * Preferences can be exported to and imported from an XML files. + * <p> + * There must be a concrete {@code PreferencesFactory} type for every concrete + * {@code Preferences} type developed. Every J2SE implementation must provide a default + * implementation for every supported platform, and must also provide a means of + * replacing the default implementation. This implementation uses the system property + * {@code java.util.prefs.PreferencesFactory} to detemine which preferences + * implementation to use. + * </p> + * <p> + * The methods of this class are thread-safe. If multiple JVMs are using the same + * back-end concurrently, the back-end won't be corrupted, but no other + * behavior guarantees are made. + * </p> + * + * @since Android 1.0 + */ +public abstract class Preferences { + + /* + * --------------------------------------------------------- + * Class fields + * --------------------------------------------------------- + */ + + /** + * Maximum size in characters allowed for a preferences key. + * + * @since Android 1.0 + */ + public static final int MAX_KEY_LENGTH = 80; + + /** + * Maximum size in characters allowed for a preferences name. + * + * @since Android 1.0 + */ + public static final int MAX_NAME_LENGTH = 80; + + /** + * Maximum size in characters allowed for a preferences value. + * + * @since Android 1.0 + */ + 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. + * + * @since Android 1.0 + */ + protected Preferences() { + super(); + } + + /* + * --------------------------------------------------------- + * Methods + * --------------------------------------------------------- + */ + + /** + * Gets the absolute path string of this preference node. + * + * @return the preference node's absolute path string. + * @since Android 1.0 + */ + public abstract String absolutePath(); + + /** + * Returns the names of all children of this node or an empty string if this + * node has no children. + * + * @return the names of all children of this node. + * @throws BackingStoreException + * if backing store is unavailable or causes an operation + * failure. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract String[] childrenNames() throws BackingStoreException; + + /** + * Removes all preferences of this node. + * + * @throws BackingStoreException + * if backing store is unavailable or causes an operation + * failure. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void clear() throws BackingStoreException; + + /** + * Exports all of the preferences of this node to a XML document using the given + * output stream. + * <p> + * This XML document uses the UTF-8 encoding and is written according to the + * DTD in its DOCTYPE declaration, which is the following: + * + * <pre> + * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> + * </pre> + * + * <i>Please note that (unlike the methods of this class that don't concern serialization), this call is not thread-safe.</i> + * </p> + * + * @param ostream + * the output stream to write the XML-formatted data to. + * @throws IOException + * if an error occurs while exporting. + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void exportNode (OutputStream ostream) throws IOException, BackingStoreException; + + /** + * Exports all of the preferences of this node and all its descendants to a XML + * document using the given output stream. + * <p> + * This XML document uses the UTF-8 encoding and is written according to the + * DTD in its DOCTYPE declaration, which is the following: + * + * <pre> + * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> + * </pre> + * + * <i>Please note that (unlike the methods of this class that don't concern serialization), this call is not thread-safe.</i> + * </p> + * + * @param ostream + * the output stream to write the XML-formatted data to. + * @throws IOException + * if an error occurs while exporting. + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void exportSubtree (OutputStream ostream) throws IOException, BackingStoreException; + + /** + * Forces all pending updates to this node and its descendants to be + * persisted in the backing store. + * <p> + * If this node has been removed, the invocation of this method only flushes + * this node, not its descendants. + * </p> + * + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + public abstract void flush() throws BackingStoreException; + + /** + * Gets the {@code String} value mapped to the given key or its default value if no + * value is mapped or no backing store is available. + * <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 no backing store is available. + * @return the preference value mapped to the given key. + * @throws IllegalStateException + * if this node has been removed. + * @throws NullPointerException + * if the parameter {@code key} is {@code null}. + * @since Android 1.0 + */ + public abstract String get (String key, String deflt); + + /** + * Gets the {@code boolean} value mapped to the given key or its default value if no + * value is mapped, if the backing store is unavailable, or if the value is invalid. + * <p> + * The only valid values are the {@code String} "true", which represents {@code true} and + * "false", which represents {@code false}, ignoring case. + * </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, if the backing store is unavailable, or if the + * value is invalid. + * @return the boolean value mapped to the given key. + * @throws IllegalStateException + * if this node has been removed. + * @throws NullPointerException + * if the parameter {@code key} is {@code null}. + * @since Android 1.0 + */ + public abstract boolean getBoolean (String key, boolean deflt); + + /** + * Gets the {@code byte} array value mapped to the given key or its default value if + * no value is mapped, if the backing store is unavailable, or if the value is an + * invalid string. + * <p> + * To be valid, the value string must be 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, if the backing store is unavailable, or if the + * value is invalid. + * @return the byte array value mapped to the given key. + * @throws IllegalStateException + * if this node has been removed. + * @throws NullPointerException + * if the parameter {@code key} is {@code null}. + * @since Android 1.0 + */ + public abstract byte[] getByteArray (String key, byte[] deflt); + + /** + * Gets the {@code double} value mapped to the given key or its default value if no + * value is mapped, if the backing store is unavailable, or if the value is an invalid + * string. + * <p> + * To be valid, the value string must be a string that can be converted to a {@code double} 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, if the backing store is unavailable, or if the + * value is invalid. + * @return the double value mapped to the given key. + * @throws IllegalStateException + * if this node has been removed. + * @throws NullPointerException + * if the parameter {@code key} is {@code null}. + * @since Android 1.0 + */ + public abstract double getDouble (String key, double deflt); + + /** + * Gets the {@code float} value mapped to the given key or its default value if no + * value is mapped, if the backing store is unavailable, or if the value is an invalid + * string. + * <p> + * To be valid, the value string must be a string that can be converted to a {@code float} 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, if the backing store is unavailable, or if the + * value is invalid. + * @return the float value mapped to the given key. + * @throws IllegalStateException + * if this node has been removed. + * @throws NullPointerException + * if the parameter {@code key} is {@code null}. + * @since Android 1.0 + */ + public abstract float getFloat (String key, float deflt); + + /** + * Gets the {@code int} value mapped to the given key or its default value if no + * value is mapped, if the backing store is unavailable, or if the value is an invalid + * string. + * <p> + * To be valid, the value string must be a string that can be converted to an {@code int} 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, if the backing store is unavailable, or if the + * value is invalid. + * @return the integer value mapped to the given key. + * @throws IllegalStateException + * if this node has been removed. + * @throws NullPointerException + * if the parameter {@code key} is {@code null}. + * @since Android 1.0 + */ + public abstract int getInt (String key, int deflt); + + /** + * Gets the {@code long} value mapped to the given key or its default value if no + * value is mapped, if the backing store is unavailable, or if the value is an invalid + * string. + * <p> + * To be valid, the value string must be a string that can be converted to a {@code long} 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, if the backing store is unavailable, or if the + * value is invalid. + * @return the long value mapped to the given key. + * @throws IllegalStateException + * if this node has been removed. + * @throws NullPointerException + * if the parameter {@code key} is {@code null}. + * @since Android 1.0 + */ + public abstract long getLong (String key, long deflt); + + /** + * Imports all the preferences from an XML document using the given input + * stream. + * <p> + * This XML document uses the UTF-8 encoding and must be written according to the + * DTD in its DOCTYPE declaration, which must be the following: + * + * <pre> + * <!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"> + * </pre> + * + * <i>Please note that (unlike the methods of this class that don't concern serialization), this call is not thread-safe.</i> + * </p> + * + * @param istream + * the input stream to read the data from. + * @throws InvalidPreferencesFormatException + * if the data read from the given input stream is not from a + * valid XML document. + * @throws IOException + * if an error occurs while importing. + * @throws SecurityException + * if {@code RuntimePermission("preferences")} is denied by a + * SecurityManager. + * @since Android 1.0 + */ + 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); + } + + /** + * Returns whether this is a user preference node. + * + * @return {@code true}, if this is a user preference node, {@code false} if + * this is a system preference node. + * @since Android 1.0 + */ + public abstract boolean isUserNode(); + + /** + * Returns all preference keys stored in this node or an empty array if no + * key was found. + * + * @return the list of all preference keys of this node. + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract String[] keys() throws BackingStoreException; + + /** + * Returns the name of this node. + * + * @return the name of this node. + * @since Android 1.0 + */ + public abstract String name(); + + /** + * Returns the preference node with the given path name. The path name can + * be relative or absolute. The requested node 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 a + * slash, otherwise it will be treated as an absolute path. + * </p> + * + * @param path + * the path name of the requested preference node. + * @return the requested preference node. + * @throws IllegalStateException + * if this node has been removed. + * @throws IllegalArgumentException + * if the path name is invalid. + * @throws NullPointerException + * if the given path is {@code null}. + * @since Android 1.0 + */ + public abstract Preferences node (String path); + + /** + * Returns whether the preference node with the given path name exists. The + * path is treated as relative to this node if it doesn't start with a slash, + * otherwise it is treated as an absolute path. + * <p> + * Please note that if this node has been removed, an invocation of this + * node will throw an {@code IllegalStateException} unless the given path is + * an empty string, which will return {@code false}. + * </p> + * + * @param path + * the path name of the preference node to query. + * @return {@code true}, if the queried preference node exists, {@code false} + * otherwise. + * @throws IllegalStateException + * if this node has been removed and the path is not an empty + * string. + * @throws IllegalArgumentException + * if the path name is invalid. + * @throws NullPointerException + * if the given path is {@code null}. + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @since Android 1.0 + */ + public abstract boolean nodeExists (String path) throws BackingStoreException; + + /** + * Returns the parent preference node of this node or {@code null} if this + * node is the root node. + * + * @return the parent preference node of this node. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract Preferences parent(); + + /** + * Adds a new preference to this node using the given key and value or + * updates the value if a preference with the given key already exists. + * + * @param key + * the preference key to be added or updated. + * @param value + * the preference value for the given key. + * @throws NullPointerException + * if the given key or value is {@code null}. + * @throws IllegalArgumentException + * if the given key's length is bigger than {@code + * MAX_KEY_LENGTH} or the value's length is bigger than {@code + * MAX_VALUE_LENGTH}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void put (String key, String value); + + /** + * Adds a new preference with a {@code boolean} value to this node using the given + * key and value or updates the value if a preference with the given key + * already exists. + * + * @param key + * the preference key to be added or updated. + * @param value + * the preference {@code boolean} value for the given key. + * @throws NullPointerException + * if the given key is {@code null}. + * @throws IllegalArgumentException + * if the given key's length is bigger than {@code + * MAX_KEY_LENGTH}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void putBoolean (String key, boolean value); + + /** + * Adds a new preference to this node using the given key and the string + * form of the given value or updates the value if a preference with the + * given key already exists. + * <p> + * The string form of the 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 preference key to be added or updated. + * @param value + * the preference value for the given key. + * @throws NullPointerException + * if the given key or value is {@code null}. + * @throws IllegalArgumentException + * if the given key's length is bigger than {@code + * MAX_KEY_LENGTH} or value's length is bigger than three + * quarters of {@code MAX_KEY_LENGTH}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void putByteArray (String key, byte[] value); + + /** + * Adds a new preference to this node using the given key and {@code double} + * value or updates the value if a preference with the + * given key already exists. + * <p> + * The value is stored in its string form, which is the result of invoking + * {@link Double#toString(double) Double.toString(double)}. + * </p> + * + * @param key + * the preference key to be added or updated. + * @param value + * the preference value for the given key. + * @throws NullPointerException + * if the given key is {@code null}. + * @throws IllegalArgumentException + * if the given key's length is bigger than {@code + * MAX_KEY_LENGTH}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void putDouble (String key, double value); + + /** + * Adds a new preference to this node using the given key and {@code float} + * value or updates the value if a preference with the + * given key already exists. + * <p> + * The value is stored in its string form, which is the result of invoking + * {@link Float#toString(float) Float.toString(float)}. + * </p> + * + * @param key + * the preference key to be added or updated. + * @param value + * the preference value for the given key. + * @throws NullPointerException + * if the given key is {@code null}. + * @throws IllegalArgumentException + * if the given key's length is bigger than {@code + * MAX_KEY_LENGTH}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void putFloat (String key, float value); + + /** + * Adds a new preference to this node using the given key and {@code int} + * value or updates the value if a preference with the + * given key already exists. + * <p> + * The value is stored in its string form, which is the result of invoking + * {@link Integer#toString(int) Integer.toString(int)}. + * </p> + * + * @param key + * the preference key to be added or updated. + * @param value + * the preference value for the given key. + * @throws NullPointerException + * if the given key is {@code null}. + * @throws IllegalArgumentException + * if the given key's length is bigger than {@code + * MAX_KEY_LENGTH}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void putInt (String key, int value); + + /** + * Adds a new preference to this node using the given key and {@code long} + * value or updates the value if a preference with the + * given key already exists. + * <p> + * The value is stored in its string form, which is the result of invoking + * {@link Long#toString(long) Long.toString(long)}. + * </p> + * + * @param key + * the preference key to be added or updated. + * @param value + * the preference value for the given key. + * @throws NullPointerException + * if the given key is {@code null}. + * @throws IllegalArgumentException + * if the given key's length is bigger than {@code + * MAX_KEY_LENGTH}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void putLong (String key, long value); + + /** + * Removes the preference mapped to the given key from this node. + * + * @param key + * the key of the preference to be removed. + * @throws NullPointerException + * if the given key is {@code null}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void remove (String key); + + /** + * Removes this preference node with all its descendants. The removal + * won't necessarily be persisted until the method {@code flush()} is invoked. + * + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @throws IllegalStateException + * if this node has been removed. + * @throws UnsupportedOperationException + * if this is a root node. + * @since Android 1.0 + */ + public abstract void removeNode() throws BackingStoreException; + + /** + * Registers a {@code NodeChangeListener} instance for this node, which will handle + * {@code NodeChangeEvent}s. {@code NodeChangeEvent}s will be fired when a child node has + * been added to or removed from this node. + * + * @param ncl + * the listener to be registered. + * @throws NullPointerException + * if the given listener is {@code null}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void addNodeChangeListener (NodeChangeListener ncl); + + /** + * Registers a {@code PreferenceChangeListener} instance for this node, which will + * handle {@code PreferenceChangeEvent}s. {@code PreferenceChangeEvent}s will be fired when + * a preference has been added to, removed from, or updated for this node. + * + * @param pcl + * the listener to be registered. + * @throws NullPointerException + * if the given listener is {@code null}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void addPreferenceChangeListener (PreferenceChangeListener pcl); + + /** + * Removes the given {@code NodeChangeListener} instance from this node. + * + * @param ncl + * the listener to be removed. + * @throws IllegalArgumentException + * if the given listener is {@code null}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void removeNodeChangeListener (NodeChangeListener ncl); + + /** + * Removes the given {@code PreferenceChangeListener} instance from this node. + * + * @param pcl + * the listener to be removed. + * @throws IllegalArgumentException + * if the given listener is {@code null}. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void removePreferenceChangeListener (PreferenceChangeListener pcl); + + /** + * Synchronizes the data of this preference node and its descendants with + * the back-end preference store. Any changes found in the back-end data should be reflected + * in this node and its descendants, and at the same time any local changes to this node and + * descendants should be persisted. + * + * @throws BackingStoreException + * if the backing store is unavailable or causes an operation + * failure. + * @throws IllegalStateException + * if this node has been removed. + * @since Android 1.0 + */ + public abstract void sync() throws BackingStoreException; + + /** + * Returns the system preference node for the package of the given class. + * The absolute path of the returned node is one slash followed by the given + * class's full package name, replacing each period character ('.') with + * a slash. For example, the absolute path of the preference associated with + * the class Object would be "/java/lang". As a special case, the unnamed + * package is associated with a preference node "/<unnamed>". This + * method will create the node and its ancestors as needed. Any nodes created + * by this method won't necessarily be persisted until the method {@code flush()} is + * invoked. + * + * @param c + * the given class. + * @return the system preference node for the package of the given class. + * @throws NullPointerException + * if the given class is {@code null}. + * @throws SecurityException + * if the {@code RuntimePermission("preferences")} is denied by + * a SecurityManager. + * @since Android 1.0 + */ + public static Preferences systemNodeForPackage (Class<?> c) { + checkSecurity(); + return factory.systemRoot().node(getNodeName(c)); + } + + /** + * Returns the root node of the system preference hierarchy. + * + * @return the system preference hierarchy root node. + * @throws SecurityException + * if the {@code RuntimePermission("preferences")} is denied by + * a SecurityManager. + * @since Android 1.0 + */ + 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); + } + + } + + /** + * Returns the user preference node for the package of the given class. + * The absolute path of the returned node is one slash followed by the given + * class's full package name, replacing each period character ('.') with + * a slash. For example, the absolute path of the preference associated with + * the class Object would be "/java/lang". As a special case, the unnamed + * package is associated with a preference node "/<unnamed>". This + * method will create the node and its ancestors as needed. Any nodes created + * by this method won't necessarily be persisted until the method {@code flush()} is + * invoked. + * + * @param c + * the given class. + * @return the user preference node for the package of the given class. + * @throws NullPointerException + * if the given class is {@code null}. + * @throws SecurityException + * if the {@code RuntimePermission("preferences")} is denied by + * a SecurityManager. + * @since Android 1.0 + */ + 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(".", "/"); + } + + /** + * Returns the root node of the user preference hierarchy. + * + * @return the user preference hierarchy root node. + * @throws SecurityException + * if the {@code RuntimePermission("preferences")} is denied by + * a SecurityManager. + * @since Android 1.0 + */ + public static Preferences userRoot() { + checkSecurity(); + return factory.userRoot(); + } + + /** + * Returns a string representation of this node. The format is "User/System + * Preference Node: " followed by this node's absolute path. + * + * @return the string representation of this node. + * @since Android 1.0 + */ + @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..e56dd95 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/PreferencesFactory.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; + +/** + * This interface is used by the {@link Preferences} class as factory class to + * create {@code Preferences} instances. This interface can be implemented and installed + * to replace the default preferences implementation. + * + * @since Android 1.0 + */ +public interface PreferencesFactory { + + /** + * Returns the root node of the preferences hierarchy for the calling user + * context. + * + * @return the user preferences hierarchy root node. + * @since Android 1.0 + */ + Preferences userRoot(); + + /** + * Returns the root node of the system preferences hierarchy. + * + * @return the system preferences hierarchy root node. + * @since Android 1.0 + */ + 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..2edfc71 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/XMLParser.java @@ -0,0 +1,621 @@ +/* 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.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; +// BEGIN android-removed +// import javax.xml.transform.TransformerException; +// END android-removed + +import org.apache.harmony.prefs.internal.nls.Messages; +// BEGIN android-removed +// import org.apache.xpath.XPathAPI; +// END android-removed +import org.w3c.dom.Document; +import org.w3c.dom.Element; +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; + +// BEGIN android-added +import java.util.ArrayList; +import org.w3c.dom.DocumentType; +import org.w3c.dom.Node; +// END android-added + +/** + * Utility class for importing and exporting {@code Preferences} data from an XML file. + * + * @since Android 1.0 + */ +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(); + // BEGIN android-changed + factory.setValidating(false); + // END android-changed + 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("<"); //$NON-NLS-1$ + break; + case '>': + sb.append(">"); //$NON-NLS-1$ + break; + case '&': + sb.append("&"); //$NON-NLS-1$ + break; + case '\\': + sb.append("'"); //$NON-NLS-1$ + break; + case '"': + sb.append("""); //$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); + } + // BEGIN android-removed + // catch (TransformerException e) { + // throw new InvalidPreferencesFormatException(e); + // } + // END android-removed + } + + private static void loadNode(Preferences prefs, Element node) { + // BEGIN android-note + // removed throw clause for TransformerException + // END android-note + // load preferences + // BEGIN android-changed + NodeList children = selectNodeList(node, "node"); //$NON-NLS-1$ + NodeList entries = selectNodeList(node, "map/entry"); //$NON-NLS-1$ + // END android-changed + 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)); + } + } + + // BEGIN android-added + // 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; + } + // END android-added + + /*************************************************************************** + * 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); + // BEGIN android-modified + NodeList entries = selectNodeList(doc + .getDocumentElement(), "entry"); //$NON-NLS-1$ + // END android-modified + 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..8a3dd33 --- /dev/null +++ b/prefs/src/main/java/java/util/prefs/package.html @@ -0,0 +1,14 @@ +<html> + <body> + <p> + This package 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. Since the underlying implementation is dependent + on the operating system, this package is designed to allow the installation + of a custom service provider implementation. + </p> + @since Android 1.0 + </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..cfc7236 --- /dev/null +++ b/prefs/src/main/java/org/apache/harmony/prefs/internal/nls/Messages.java @@ -0,0 +1,140 @@ +/* + * 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. + */ + +// BEGIN android-note +// Redundant code has been removed and is now called from MsgHelp. +// END android-note + +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 + } + + // BEGIN android-note + // Duplicate code was dropped in favor of using MsgHelp. + // END android-note +} 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/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..f53a579 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/AbstractPreferencesTest.java @@ -0,0 +1,1468 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed 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 dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; +import dalvik.annotation.TestTargetClass; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.prefs.AbstractPreferences; +import java.util.prefs.BackingStoreException; +import java.util.prefs.InvalidPreferencesFormatException; +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; + +@TestTargetClass(AbstractPreferences.class) +public class AbstractPreferencesTest extends TestCase { + + AbstractPreferences pref; + + static AbstractPreferences root = (AbstractPreferences) Preferences.userRoot(); + + static final String nodeName = "mock"; + + static AbstractPreferences parent = null; + + protected void setUp() throws Exception { + super.setUp(); + + parent = (AbstractPreferences) Preferences.userNodeForPackage(this.getClass()); +/* + String str[] = parent.childrenNames(); + for (int i = 0; i < str.length; i++) { + System.out.print(str[i] + " "); + } + System.out.println(); +/**/ + pref = (AbstractPreferences) parent.node(nodeName); + } + + protected void tearDown() throws Exception { +/* String str[] = parent.childrenNames(); + for (int i = 0; i < str.length; i++) { + System.out.print(str[i] + " "); + } + System.out.println();/**/ + parent.removeNode(); + super.tearDown(); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "toString", + args = {} + ) + public void testToString() { + assertTrue(pref.toString().contains(nodeName)); + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Tests putSpi indirectly", + method = "put", + args = {java.lang.String.class, java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Tests putSpi indirectly", + method = "putSpi", + args = {java.lang.String.class, java.lang.String.class} + ) + }) + public void testPut() throws BackingStoreException { + pref.put("Value", "String"); + pref.flush(); + + assertEquals("String", pref.get("Value", ":")); + + try { + pref.put(null, "Exception"); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + int i; + StringBuffer sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_KEY_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.put(new String(sb), "Exception"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_VALUE_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.put("DoubleValue", new String(sb)); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.put("DoubleValue", "Exception"); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "getSpi tested indirectly.", + method = "get", + args = {java.lang.String.class, java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "getSpi tested indirectly.", + method = "getSpi", + args = {java.lang.String.class} + ) + }) + public void testGet() throws BackingStoreException { + pref.put("Value", "String"); + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.putBoolean("BoolValue", true); + pref.flush(); + + assertEquals("String", pref.get("Value", ":")); + assertEquals("true", pref.get("BoolValue", ":")); + assertEquals("9.10938188E-31", pref.get("DoubleValue", null)); + + try { + pref.get(null, "Exception"); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + pref.removeNode(); + + try { + pref.get("DoubleValue", "Exception"); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Indirectly checks removeSpi", + method = "remove", + args = {java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Indirectly checks removeSpi", + method = "removeSpi", + args = {java.lang.String.class} + ) + }) + public void testRemove() throws BackingStoreException { + String[] keyArray = new String[]{"Value", "DoubleValue", "LongValue", "IntValue"}; + pref.put(keyArray[0], "String"); + pref.putDouble(keyArray[1], new Double(9.10938188e-31)); + pref.putLong(keyArray[2], new Long(Long.MIN_VALUE)); + pref.putInt(keyArray[3], 299792458); + pref.node("New node"); + pref.flush(); + + String[] str = pref.keys(); + assertEquals(keyArray.length, str.length); + for(int i = 0; i < keyArray.length; i++) { + pref.remove(keyArray[i]); + str = pref.keys(); + assertEquals(keyArray.length - i - 1, str.length); + } + assertEquals(1, pref.childrenNames().length); + pref.remove("New node"); + assertEquals(1, pref.childrenNames().length); + + pref.removeNode(); + + try { + pref.remove("New node"); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "clear", + args = {} + ) + public void testClear() throws BackingStoreException { + AbstractPreferences ap = (AbstractPreferences) pref.node("New node"); + pref.putInt("IntValue", 33); + pref.putBoolean("BoolValue", true); + pref.flush(); + assertTrue(pref.getBoolean("BoolValue", false)); + assertEquals(33, pref.getInt("IntValue", 22)); + assertEquals(1, pref.childrenNames().length); + pref.clear(); + assertFalse(pref.getBoolean("BoolValue", false)); + assertEquals(22, pref.getInt("IntValue", 22)); + assertEquals(1, pref.childrenNames().length); + + pref.removeNode(); + + try { + pref.clear(); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + + try { + ap.clear(); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "putInt", + args = {java.lang.String.class, int.class} + ) + public void testPutInt() throws BackingStoreException { + pref.putInt("IntValue", 299792458); + pref.flush(); + + assertEquals(299792458, pref.getInt("IntValue", new Integer(1))); + + try { + pref.putInt(null, new Integer(1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + int i; + StringBuffer sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_KEY_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.putInt(new String(sb), new Integer(1)); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.putInt("IntValue", new Integer(1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getInt", + args = {java.lang.String.class, int.class} + ) + public void testGetInt() throws BackingStoreException { + pref.put("Value", "String"); + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.putLong("LongValue", new Long(Long.MIN_VALUE)); + pref.putInt("IntValue", 299792458); + pref.flush(); + + assertEquals(1, pref.getInt("Value", new Integer(1))); + assertEquals(1, pref.getInt("LongValue", new Integer(1))); + assertEquals(1, pref.getInt("DoubleValue", new Integer(1))); + assertEquals(299792458, pref.getInt("IntValue", new Integer(1))); + + try { + pref.getInt(null, new Integer(1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + pref.removeNode(); + + try { + pref.getInt("IntValue", new Integer(1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "putLong", + args = {java.lang.String.class, long.class} + ) + public void testPutLong() throws BackingStoreException { + pref.putLong("LongValue", new Long(299792458)); + pref.flush(); + + assertEquals(299792458L, pref.getLong("LongValue", new Long(1))); + + try { + pref.putLong(null, new Long(1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + int i; + StringBuffer sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_KEY_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.putLong(new String(sb), new Long(1)); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.putLong("LongValue", new Long(1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getLong", + args = {java.lang.String.class, long.class} + ) + public void testGetLong() throws BackingStoreException { + pref.put("Value", "String"); + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.putLong("LongValue", new Long(Long.MIN_VALUE)); + pref.putInt("IntValue", 299792458); + pref.flush(); + + assertEquals(1L, pref.getLong("Value", new Long(1))); + assertEquals(Long.MIN_VALUE, pref.getLong("LongValue", new Long(1))); + assertEquals(1L, pref.getLong("DoubleValue", new Long(1))); + assertEquals(299792458L, pref.getLong("IntValue", new Long(1))); + + try { + pref.getLong(null, new Long(1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + pref.removeNode(); + + try { + pref.getLong("LongValue", new Long(1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "putBoolean", + args = {java.lang.String.class, boolean.class} + ) + public void testPutBoolean() throws BackingStoreException { + pref.putBoolean("BoolValue", true); + pref.flush(); + + assertTrue(pref.getBoolean("BoolValue", false)); + + try { + pref.putBoolean(null, true); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + int i; + StringBuffer sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_KEY_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.putBoolean(new String(sb), true); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.putBoolean("DoubleValue", true); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getBoolean", + args = {java.lang.String.class, boolean.class} + ) + public void testGetBoolean() throws BackingStoreException { + pref.put("Value", "String"); + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.putBoolean("BoolValue", true); + pref.flush(); + + assertFalse(pref.getBoolean("Value", false)); + assertTrue(pref.getBoolean("BoolValue", false)); + assertFalse(pref.getBoolean("DoubleValue", false)); + + try { + pref.getBoolean(null, true); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + pref.removeNode(); + + try { + pref.getBoolean("DoubleValue", true); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "putFloat", + args = {java.lang.String.class, float.class} + ) + public void testPutFloat() throws BackingStoreException { + pref.putFloat("FloatValue", new Float(1.602e-19)); + pref.flush(); + + assertEquals(new Float(1.602e-19), pref.getFloat("FloatValue", new Float(0.2))); + + try { + pref.putFloat(null, new Float(0.1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + int i; + StringBuffer sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_KEY_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.putFloat(new String(sb), new Float(0.1)); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.putFloat("FloatValue", new Float(0.1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getFloat", + args = {java.lang.String.class, float.class} + ) + public void testGetFloat() throws BackingStoreException { + pref.put("Value", "String"); + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.putFloat("FloatValue", new Float(-0.123)); + pref.putInt("IntValue", 299792458); + pref.flush(); + + assertEquals(new Float(0.1), pref.getFloat("Value", new Float(0.1))); + assertEquals(new Float(-0.123), pref.getFloat("FloatValue", new Float(0.2))); + assertEquals(new Float(9.109382e-31), pref.getFloat("DoubleValue", new Float(2.14))); + assertEquals(new Float(2.99792448e8), pref.getFloat("IntValue", new Float(5))); + + try { + pref.getFloat(null, new Float(0.1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + pref.removeNode(); + + try { + pref.getFloat("FloatValue", new Float(0.1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "putDouble", + args = {java.lang.String.class, double.class} + ) + public void testPutDouble() throws BackingStoreException { + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.flush(); + + assertEquals(new Double(9.10938188e-31), pref.getDouble("DoubleValue", new Double(2.14))); + + try { + pref.putDouble(null, new Double(0.1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + int i; + StringBuffer sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_KEY_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.putDouble(new String(sb), new Double(0.1)); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.putDouble("DoubleValue", new Double(0.1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getDouble", + args = {java.lang.String.class, double.class} + ) + public void testGetDouble() throws BackingStoreException { + pref.put("Value", "String"); + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.putBoolean("BoolValue", true); + pref.putInt("IntValue", 299792458); + pref.flush(); + + assertEquals(new Double(0.1), pref.getDouble("Value", new Double(0.1))); + assertEquals(new Double(0.2), pref.getDouble("BoolValue", new Double(0.2))); + assertEquals(new Double(9.10938188e-31), pref.getDouble("DoubleValue", new Double(2.14))); + assertEquals(new Double(2.99792458e8), pref.getDouble("IntValue", new Double(5))); + + try { + pref.getDouble(null, new Double(0.1)); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + pref.removeNode(); + + try { + pref.getDouble("DoubleValue", new Double(0.1)); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "putByteArray", + args = {java.lang.String.class, byte[].class} + ) + public void testPutByteArray() throws BackingStoreException { + byte[] bArray = new byte[]{1, 2, 3, 4, 5}; + byte[] array = null; + int i; + pref.putByteArray("Array", bArray); + pref.flush(); + + array = pref.getByteArray("Array", null); + assertEquals(bArray.length, array.length); + for(i = 0; i < bArray.length; i++) { + assertEquals(bArray[i], array[i]); + } + + try { + pref.putByteArray(null, bArray); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + StringBuffer sb = new StringBuffer(); + + for (i = 0; i < Preferences.MAX_KEY_LENGTH + 1; i++) { + sb.append('c'); + } + + try { + pref.putByteArray(new String(sb), bArray); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + bArray = new byte[Preferences.MAX_VALUE_LENGTH * 3 / 4 + 1]; + + try { + pref.putByteArray("Big array", bArray); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.putByteArray("Array", new byte[10]); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "getByteArray", + args = {java.lang.String.class, byte[].class} + ) + public void testGetByteArray() throws BackingStoreException { + byte[] bArray = new byte[]{1, 2, 3, 4, 5}; + byte[] tmp = new byte[]{5}; + byte[] array = null; + int i; + pref.put("Value", "String"); + pref.putDouble("DoubleValue", new Double(9.10938188e-31)); + pref.putByteArray("Array", bArray); + pref.flush(); + + array = pref.getByteArray("Value", tmp); + assertEquals(tmp.length, array.length); + for(i = 0; i < tmp.length; i++) { + assertEquals(tmp[i], array[i]); + } + + array = pref.getByteArray("DoubleValue", tmp); + assertEquals(tmp.length, array.length); + for(i = 0; i < tmp.length; i++) { + assertEquals(tmp[i], array[i]); + } + + array = pref.getByteArray("Array", tmp); + assertEquals(bArray.length, array.length); + for(i = 0; i < bArray.length; i++) { + assertEquals(bArray[i], array[i]); + } + + try { + pref.getByteArray(null, tmp); + fail("NullPointerException expected"); + } catch (NullPointerException e) { + //expected + } + + pref.removeNode(); + + try { + pref.getByteArray("Array", tmp); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "keysSpi tested indirectly", + method = "keys", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "keysSpi tested indirectly", + method = "keysSpi", + args = {} + ) + }) + public void testKeys() throws BackingStoreException { + String[] keyArray = new String[]{"Value", "DoubleValue", "BoolValue", "IntValue"}; + String nodeStr = "New node"; + pref.node(nodeStr); + pref.put(keyArray[0], "String"); + pref.putDouble(keyArray[1], new Double(9.10938188e-31)); + pref.putBoolean(keyArray[2], true); + pref.putInt(keyArray[3], 299792458); + pref.flush(); + + String[] str = pref.keys(); + assertEquals(keyArray.length, str.length); + for(int i = 0; i < str.length; i++) { + boolean flag = false; + for(int j = 0; j < keyArray.length; j++) { + if (str[i].compareTo(keyArray[j]) == 0) { + flag = true; + break; + } + } + assertTrue(str[i].compareTo(nodeStr) != 0); + assertTrue(flag); + } + + pref.removeNode(); + + try { + pref.keys(); + fail("IllegalStateException expected"); + } catch(IllegalStateException e) { + //expected + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException can not be checked. childrenNamesSpi checked indirectly.", + method = "childrenNames", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException can not be checked. childrenNamesSpi checked indirectly.", + method = "childrenNamesSpi", + args = {} + ) + }) + public void testChildrenNames() throws BackingStoreException { + AbstractPreferences first = (AbstractPreferences) pref.node("First node"); + AbstractPreferences second = (AbstractPreferences) pref.node("Second node"); + + assertEquals(2, pref.childrenNames().length); + assertEquals(0, first.childrenNames().length); + assertEquals(0, second.childrenNames().length); + + second.removeNode(); + + try { + second.childrenNames(); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + + pref.removeNode(); + + try { + first.childrenNames(); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "parent", + args = {} + ) + public void testParent() throws BackingStoreException { + AbstractPreferences node = (AbstractPreferences) pref.node("First node/sub node"); + + assertTrue(node.parent().name().compareTo("First node") == 0); + + pref.removeNode(); + + try { + node.parent(); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Indirecly checks childSpi", + method = "node", + args = {java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Indirecly checks childSpi", + method = "childSpi", + args = {java.lang.String.class} + ) + }) + public void testNode() throws BackingStoreException { + AbstractPreferences first = (AbstractPreferences) pref.node("First node"); + AbstractPreferences second = (AbstractPreferences) pref.node("Second node"); + + try { + first.node("blabla/"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + try { + first.node("///invalid"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + StringBuffer sb = new StringBuffer(); + + for (int i = 0; i < Preferences.MAX_NAME_LENGTH; i++) { + sb.append('c'); + } + first.node(new String(sb)); + sb.append('c'); + + try { + first.node(new String(sb)); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + second.removeNode(); + + try { + second.node(""); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + pref.removeNode(); + try { + first.node(""); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "getChild tested indirectly", + method = "nodeExists", + args = {java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "getChild tested indirectly", + method = "getChild", + args = {java.lang.String.class} + ) + }) + public void testNodeExists() throws BackingStoreException { + AbstractPreferences ap1 = (AbstractPreferences) pref.node("First node"); + AbstractPreferences ap2 = (AbstractPreferences) pref.node("Second node"); + pref.putInt("IntegerValue", 33); + pref.putBoolean("BoolValue", true); + pref.flush(); + + assertTrue(pref.nodeExists("First node")); + assertTrue(pref.nodeExists("Second node")); + assertFalse(pref.nodeExists("IntegerValue")); + assertFalse(pref.nodeExists("BoolValue")); + assertFalse(pref.nodeExists("Value")); + assertFalse(pref.nodeExists(nodeName)); + + try { + pref.nodeExists("///invalid"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + pref.removeNode(); + + try { + pref.nodeExists("Exception"); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "removeNode", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "removeNodeSpi", + args = {} + ) + }) + public void testRemoveNode() throws BackingStoreException { + String[] nodeArray = new String[]{"First node", "Second node", "Last node"}; + int i; + pref.put("Key", "String"); + for (i = 0; i < nodeArray.length; i++) { + pref.node(nodeArray[i]); + } + pref.flush(); + + String[] str = pref.childrenNames(); + assertEquals(nodeArray.length, str.length); + for(i = 0; i < nodeArray.length; i++) { + pref.node(nodeArray[i]).removeNode(); + str = pref.childrenNames(); + assertEquals(nodeArray.length - i - 1, str.length); + } + assertEquals(1, pref.keys().length); + pref.node("Key").removeNode(); + assertEquals(1, pref.keys().length); + + pref.removeNode(); + + try { + pref.removeNode(); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + + try { + root.removeNode(); + fail("UnsupportedOperationException expected"); + } catch (UnsupportedOperationException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "name", + args = {} + ) + public void testName() { + AbstractPreferences first = (AbstractPreferences) pref.node("First node"); + AbstractPreferences second = (AbstractPreferences) pref.node("Second node/sub node"); + + assertTrue(first.name().compareTo("First node") == 0); + assertFalse(first.name().compareTo("Second node") == 0); + assertTrue(second.name().compareTo("sub node") == 0); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "absolutePath", + args = {} + ) + public void testAbsolutePath() { + assertEquals(parent.absolutePath() + "/" + nodeName, pref.absolutePath()); + assertEquals(parent.absolutePath() + "/" + "new node", parent.node("new node").absolutePath()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "isUserNode", + args = {} + ) + public void testIsUserNode() { + assertTrue(parent.isUserNode()); + assertFalse(Preferences.systemRoot().isUserNode()); + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Indirectly checks syncSpi", + method = "sync", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Indirectly checks syncSpi", + method = "syncSpi", + args = {} + ) + }) + public void testSync() throws BackingStoreException { + pref.node("new node/sub node"); + pref.sync(); + + pref.removeNode(); + + try { + pref.sync(); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + } + + class MockPreferenceChangeListener implements PreferenceChangeListener { + private boolean flagChange = false; + + public void preferenceChange(PreferenceChangeEvent arg0) { + flagChange = true; + } + + public boolean isChanged () { + boolean retVal = flagChange; + flagChange = false; + return retVal; + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "addPreferenceChangeListener", + args = {java.util.prefs.PreferenceChangeListener.class} + ) + public void testAddPreferenceChangeListener() throws BackingStoreException { + MockPreferenceChangeListener mpcl = new MockPreferenceChangeListener(); + parent.addPreferenceChangeListener(mpcl); + assertFalse(mpcl.isChanged()); + pref.node("new node"); + pref.flush(); + parent.flush(); + assertFalse(mpcl.isChanged()); + parent.node("new node"); + parent.flush(); + assertFalse(mpcl.isChanged()); + parent.putInt("IntValue", 33); + parent.flush(); + parent.flush(); + assertTrue(mpcl.isChanged()); + assertEquals(33, parent.getInt("IntValue", 22)); + parent.flush(); + assertFalse(mpcl.isChanged()); + assertEquals(22, parent.getInt("Missed Value", 22)); + parent.flush(); + assertFalse(mpcl.isChanged()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "removePreferenceChangeListener", + args = {java.util.prefs.PreferenceChangeListener.class} + ) + public void testRemovePreferenceChangeListener() throws BackingStoreException { + MockPreferenceChangeListener mpcl = new MockPreferenceChangeListener(); + parent.addPreferenceChangeListener(mpcl); + assertFalse(mpcl.isChanged()); + parent.putInt("IntValue", 33); + parent.flush(); + assertTrue(mpcl.isChanged()); + parent.removePreferenceChangeListener(mpcl); + parent.putInt("IntValue", 33); + parent.flush(); + assertFalse(mpcl.isChanged()); + } + + class MockNodeChangeListener implements NodeChangeListener { + private boolean flagAdded = false; + private boolean flagRemoved = false; + + public void childAdded(NodeChangeEvent arg0) { + flagAdded = true; + } + + public void childRemoved(NodeChangeEvent arg0) { + flagRemoved = true; + } + + public boolean isAdded() { + return flagAdded; + } + + public boolean isRemoved() { + return flagRemoved; + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "addNodeChangeListener", + args = {java.util.prefs.NodeChangeListener.class} + ) + public void testAddNodeChangeListener() throws BackingStoreException { + MockNodeChangeListener mncl = new MockNodeChangeListener(); + parent.addNodeChangeListener(mncl); + pref.node("test"); + pref.flush(); + parent.flush(); + assertFalse(mncl.isAdded()); + assertFalse(mncl.isRemoved()); + pref.removeNode(); + parent.flush(); + assertFalse(mncl.isAdded()); + assertTrue(mncl.isRemoved()); + parent.node("new node"); + parent.flush(); + assertTrue(mncl.isAdded()); + assertTrue(mncl.isRemoved()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "removeNodeChangeListener", + args = {java.util.prefs.NodeChangeListener.class} + ) + public void testRemoveNodeChangeListener() throws BackingStoreException { + MockNodeChangeListener mncl = new MockNodeChangeListener(); + parent.addNodeChangeListener(mncl); + pref.node("test"); + pref.flush(); + parent.flush(); + assertFalse(mncl.isAdded()); + assertFalse(mncl.isRemoved()); + parent.removeNodeChangeListener(mncl); + pref.removeNode(); + parent.flush(); + assertFalse(mncl.isAdded()); + assertFalse(mncl.isRemoved()); + parent.node("new node"); + parent.flush(); + assertFalse(mncl.isAdded()); + assertFalse(mncl.isRemoved()); + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException, IOException can not be checked.", + method = "exportNode", + args = {java.io.OutputStream.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException, IOException can not be checked.", + method = "flush", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException, IOException can not be checked.", + method = "flushSpi", + args = {} + ) + }) + public void testExportNode() throws BackingStoreException, IOException, InvalidPreferencesFormatException { + AbstractPreferences ap = (AbstractPreferences) pref.node("New node"); + pref.putInt("IntValue", 33); + pref.putBoolean("BoolValue", true); + pref.flush(); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + pref.exportNode(baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + + assertTrue(pref.getBoolean("BoolValue", false)); + assertEquals(33, pref.getInt("IntValue", 22)); + assertEquals(1, pref.childrenNames().length); + + String xmlData = new String(baos.toByteArray()); + + assertTrue(xmlData.contains("IntValue")); + assertTrue(xmlData.contains("BoolValue")); + assertTrue(xmlData.contains("33")); + assertTrue(xmlData.contains("true")); + + pref.removeNode(); + + try { + pref.exportNode(new ByteArrayOutputStream()); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + + try { + pref.getBoolean("BoolValue", false); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + pref = (AbstractPreferences) parent.node(nodeName); + + pref.importPreferences(bais); + + assertTrue(pref.getBoolean("BoolValue", false)); + assertEquals(33, pref.getInt("IntValue", 22)); + assertEquals(0, pref.childrenNames().length); + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException, IOException can not be checked.", + method = "exportSubtree", + args = {java.io.OutputStream.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException, IOException can not be checked.", + method = "flush", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "BackingStoreException, IOException can not be checked.", + method = "flushSpi", + args = {} + ) + }) + public void testExportSubtree() throws BackingStoreException, IOException, InvalidPreferencesFormatException { + AbstractPreferences ap1 = (AbstractPreferences) pref.node("First node"); + AbstractPreferences ap2 = (AbstractPreferences) pref.node("Second node"); + pref.putInt("IntegerValue", 33); + pref.putBoolean("BoolValue", true); + pref.flush(); + + ap1.putInt("FirstIntValue", 11); + ap2.putDouble("DoubleValue", new Double(6.626e-34)); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + pref.exportSubtree(baos); + ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); + + assertTrue(pref.getBoolean("BoolValue", false)); + assertEquals(33, pref.getInt("IntegerValue", 22)); + assertEquals(2, pref.childrenNames().length); + assertEquals(11, ap1.getInt("FirstIntValue", 22)); + assertEquals(new Double(6.626e-34), ap2.getDouble("DoubleValue", new Double (3.14))); + + String xmlData = new String(baos.toByteArray()); + + assertTrue(xmlData.contains("IntegerValue")); + assertTrue(xmlData.contains("BoolValue")); + assertTrue(xmlData.contains("FirstIntValue")); + assertTrue(xmlData.contains("DoubleValue")); + assertTrue(xmlData.contains("33")); + assertTrue(xmlData.contains("true")); + assertTrue(xmlData.contains("11")); + assertTrue(xmlData.contains("6.626E-34")); + + pref.removeNode(); + + try { + pref.exportSubtree(new ByteArrayOutputStream()); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + + try { + pref.getBoolean("BoolValue", false); + fail("IllegalStateException expected"); + } catch (IllegalStateException e) { + //expected + } + pref = (AbstractPreferences) parent.node(nodeName); + pref.importPreferences(bais); + + ap1 = (AbstractPreferences) pref.node("First node"); + ap2 = (AbstractPreferences) pref.node("Second node"); + + assertTrue(pref.getBoolean("BoolValue", false)); + assertEquals(33, pref.getInt("IntegerValue", 22)); + assertEquals(2, pref.childrenNames().length); + assertEquals(11, ap1.getInt("FirstIntValue", 22)); + assertEquals(new Double(6.626e-34), ap2.getDouble("DoubleValue", new Double (3.14))); + } + + class MockAbstractPreferences extends AbstractPreferences { + protected MockAbstractPreferences(AbstractPreferences parent, String name) { + super(parent, name); + } + + @Override + protected AbstractPreferences childSpi(String name) { + return null; + } + + @Override + protected String[] childrenNamesSpi() throws BackingStoreException { + return null; + } + + @Override + protected void flushSpi() throws BackingStoreException { + } + + @Override + protected String getSpi(String key) { + return null; + } + + @Override + protected String[] keysSpi() throws BackingStoreException { + return null; + } + + @Override + protected void putSpi(String key, String value) { + } + + @Override + protected void removeNodeSpi() throws BackingStoreException { + } + + @Override + protected void removeSpi(String key) { + } + + @Override + protected void syncSpi() throws BackingStoreException { + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "AbstractPreferences", + args = {java.util.prefs.AbstractPreferences.class, java.lang.String.class} + ) + public void testAbstractPreferences() { + assertNotNull(new MockAbstractPreferences(pref, "node name")); + try { + new MockAbstractPreferences(pref, "node/name"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + + try { + new MockAbstractPreferences(null, "node"); + fail("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { + //expected + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Tested indirectly", + method = "cachedChildren", + args = {} + ) + public void testCachedChildren() throws BackingStoreException { + pref.node("First node"); + pref.node("Second node"); + + assertEquals(2, pref.childrenNames().length); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "No reason to check dummy implementation", + method = "isRemoved", + args = {} + ) + public void testIsRemoved() { + } +} 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..29ff362 --- /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 = tests.TestSuiteFactory.createTestSuite("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..a98c3b4 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/BackingStoreExceptionTest.java @@ -0,0 +1,96 @@ +/* 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 dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +import java.util.prefs.BackingStoreException; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; + +/** + * + * + */ +@TestTargetClass(BackingStoreException.class) +public class BackingStoreExceptionTest extends TestCase { + + /* + * Class under test for void BackingStoreException(String) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "BackingStoreException", + args = {java.lang.String.class} + ) + public void testBackingStoreExceptionString() { + BackingStoreException e = new BackingStoreException("msg"); + assertNull(e.getCause()); + assertEquals("msg", e.getMessage()); + } + + /* + * Class under test for void BackingStoreException(Throwable) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "BackingStoreException", + args = {java.lang.Throwable.class} + ) + 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. + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Verifies serialization", + method = "!SerializationSelf", + args = {} + ) + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new BackingStoreException("msg")); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Verifies serialization", + method = "!SerializationGolden", + args = {} + ) + 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..a5deaed --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/FilePreferencesImplTest.java @@ -0,0 +1,286 @@ +/* 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 dalvik.annotation.AndroidOnly; +import dalvik.annotation.BrokenTest; +import dalvik.annotation.KnownFailure; +import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +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; + +@TestTargetClass(java.util.prefs.Preferences.class) +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; + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Exceptions checking missed, but method is abstract, probably it is OK", + method = "put", + args = {java.lang.String.class, java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Exceptions checking missed, but method is abstract, probably it is OK", + method = "get", + args = {java.lang.String.class, java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Exceptions checking missed, but method is abstract, probably it is OK", + method = "keys", + args = {} + ) + }) + 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)); + } + + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Exceptions checking missed, but method is abstract, probably it is OK", + method = "childrenNames", + args = {} + ) + @AndroidOnly("Checking of childNames.length doesn't pass on RI because of " + + "it depends on .userPrefs properties.") + @KnownFailure("This test fails on emulator. " + + "java.util.prefs.BackingStoreException is thrown during calling of" + + "childrenNames() method.") + 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(); + for (int i = 0; i < childNames.length; i++) { + System.out.println("test:" + childNames[i]); + } + assertEquals(4, 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]); + } + + 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]); + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "SecurityException checking only, but methods are abstract, probably it is OK", + method = "node", + args = {java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "SecurityException checking only, but methods are abstract, probably it is OK", + method = "removeNode", + args = {} + ), + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "SecurityException checking only, but methods are abstract, probably it is OK", + method = "childrenNames", + args = {} + ), + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "SecurityException checking only, but methods are abstract, probably it is OK", + method = "flush", + args = {} + ), + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "SecurityException checking only, but methods are abstract, probably it is OK", + method = "sync", + args = {} + ) + }) + 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..28c953e --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/InvalidPreferencesFormatExceptionTest.java @@ -0,0 +1,119 @@ +/* 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 dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +import java.util.prefs.InvalidPreferencesFormatException; + +import junit.framework.TestCase; + +import org.apache.harmony.testframework.serialization.SerializationTest; + +/** + * + */ +@TestTargetClass(InvalidPreferencesFormatException.class) +public class InvalidPreferencesFormatExceptionTest extends TestCase { + + /* + * Class under test for void InvalidPreferencesFormatException(String) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "InvalidPreferencesFormatException", + args = {java.lang.String.class} + ) + public void testInvalidPreferencesFormatExceptionString() { + InvalidPreferencesFormatException e = new InvalidPreferencesFormatException( + "msg"); + assertNull(e.getCause()); + assertEquals("msg", e.getMessage()); + } + + /* + * Class under test for void InvalidPreferencesFormatException(String, + * Throwable) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "InvalidPreferencesFormatException", + args = {java.lang.String.class, java.lang.Throwable.class} + ) + 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) + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "InvalidPreferencesFormatException", + args = {java.lang.Throwable.class} + ) + 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. + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Verifies serialization", + method = "!SerializationSelf", + args = {} + ) + public void testSerializationSelf() throws Exception { + + SerializationTest.verifySelf(new InvalidPreferencesFormatException( + "msg")); + } + + /** + * @tests serialization/deserialization compatibility with RI. + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Verifies serialization", + method = "!SerializationGolden", + args = {} + ) + 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..1820954 --- /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..6e042fa --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockNodeChangeListener.java @@ -0,0 +1,132 @@ +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 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 waitForEvent() { + try { + synchronized (addLock) { + addLock.wait(500); + } + } catch (InterruptedException e) { + } + } + + 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; + 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++; + removeLock.notifyAll(); + } + } + + public boolean getAddResult() { + synchronized (addLock) { + return addResult; + } + } + + public boolean getRemoveResult() { + synchronized (removeLock) { + return removeResult; + } + } + + public int getAdded() { + synchronized (addLock) { + return added; + } + } + + public int getRemoved() { + synchronized (removeLock) { + 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..9d628a2 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockPreferenceChangeListener.java @@ -0,0 +1,118 @@ +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; + } + + public void waitForEvent() { + waitForEvent(1); + } + + + public void waitForEvent(int count) { + for (int i = 0; i < count; i++) { + try { + synchronized (lock) { + lock.wait(500); + } + } catch (InterruptedException e) { + } + } + } + + // 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/MockSecurityManager.java b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/MockSecurityManager.java new file mode 100644 index 0000000..e5a0bfd --- /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..3260a04 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeEventTest.java @@ -0,0 +1,155 @@ +/* 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 dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +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; + +/** + * + */ +@TestTargetClass(NodeChangeEvent.class) +public class NodeChangeEventTest extends TestCase { + + NodeChangeEvent event; + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "NodeChangeEvent", + args = {java.util.prefs.Preferences.class, java.util.prefs.Preferences.class} + ) + 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()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "NodeChangeEvent", + args = {java.util.prefs.Preferences.class, java.util.prefs.Preferences.class} + ) + 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()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Verifies serialization", + method = "!Serialization", + args = {} + ) + public void testSerialization() throws Exception { + + event = new NodeChangeEvent(Preferences.systemRoot(), null); + + try { + SerializationTest.copySerializable(event); + fail("No expected NotSerializableException"); + } catch (NotSerializableException e) { + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test is correct, functionality checked in separate Mock class.", + method = "getChild", + args = {} + ) + 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"); + nl.waitForEvent(); + assertEquals(1, nl.getAdded()); + assertTrue(nl.getAddResult()); + nl.reset(); + child1.removeNode(); + nl.waitForEvent(); + assertEquals(1, nl.getRemoved()); + assertTrue(nl.getRemoveResult()); + nl.reset(); + } finally { + pref.removeNodeChangeListener(nl); + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test is correct, functionality checked in separate Mock class.", + method = "getParent", + args = {} + ) + 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"); + nl.waitForEvent(); + assertEquals(1, nl.getAdded()); + assertTrue(nl.getAddResult()); + nl.reset(); + child1.removeNode(); + nl.waitForEvent(); + 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..c5e3252 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/NodeChangeListenerTest.java @@ -0,0 +1,85 @@ +/* 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 dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +import java.util.prefs.NodeChangeEvent; +import java.util.prefs.NodeChangeListener; +import java.util.prefs.Preferences; + +import junit.framework.TestCase; + +/** + * + */ +@TestTargetClass(NodeChangeListener.class) +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(); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Testing Interface", + method = "childAdded", + args = {java.util.prefs.NodeChangeEvent.class} + ) + public void testChildAdded() { + l.childAdded(new NodeChangeEvent(Preferences.userRoot(), Preferences + .userRoot())); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Testing Interface", + method = "childRemoved", + args = {java.util.prefs.NodeChangeEvent.class} + ) + 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..4030b89 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeEventTest.java @@ -0,0 +1,199 @@ +/* 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 dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +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; + +/** + * + */ +@TestTargetClass(PreferenceChangeEvent.class) +public class PreferenceChangeEventTest extends TestCase { + + PreferenceChangeEvent event; + + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Checks exception.", + method = "PreferenceChangeEvent", + args = {java.util.prefs.Preferences.class, java.lang.String.class, java.lang.String.class} + ) + public void testPreferenceChangeEventException() { + try { + event = new PreferenceChangeEvent(null, "key", "value"); + fail(); + } catch (IllegalArgumentException e) { + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "PreferenceChangeEvent", + args = {java.util.prefs.Preferences.class, java.lang.String.class, java.lang.String.class} + ) + 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()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "PreferenceChangeEvent", + args = {java.util.prefs.Preferences.class, java.lang.String.class, java.lang.String.class} + ) + 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()); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Verifies serialization", + method = "!Serialization", + args = {} + ) + public void testSerialization() throws Exception { + event = new PreferenceChangeEvent(Preferences.userRoot(), "key", + "value"); + try { + SerializationTest.copySerializable(event); + fail("No expected NotSerializableException"); + } catch (NotSerializableException e) { + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test is correct, functionality checked in separate Mock class.", + method = "getKey", + args = {} + ) + 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); + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test is correct, functionality checked in separate Mock class.", + method = "getNewValue", + args = {} + ) + 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); + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test is correct, functionality checked in separate Mock class.", + method = "getNode", + args = {} + ) + 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..e4df9c4 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferenceChangeListenerTest.java @@ -0,0 +1,64 @@ +/* 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 dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; + +import junit.framework.TestCase; + +/** + * + */ +@TestTargetClass(PreferenceChangeListener.class) +public class PreferenceChangeListenerTest extends TestCase { + + PreferenceChangeListener l; + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + l = new PreferenceChangeListenerImpl(); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Testing Interface", + method = "preferenceChange", + args = {java.util.prefs.PreferenceChangeEvent.class} + ) + 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..818d5ad --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesFactoryTest.java @@ -0,0 +1,77 @@ +/* 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 dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargets; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetNew; + +import java.util.prefs.Preferences; +import java.util.prefs.PreferencesFactory; + +import junit.framework.TestCase; + +/** + * + */ +@TestTargetClass(PreferencesFactory.class) +public class PreferencesFactoryTest extends TestCase { + + PreferencesFactory f; + + /* + * @see TestCase#setUp() + */ + protected void setUp() throws Exception { + super.setUp(); + f = new PreferencesFactoryImpl(); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Testing Interface", + method = "userRoot", + args = {} + ) + public void testUserRoot() { + f.userRoot(); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Testing Interface", + method = "systemRoot", + args = {} + ) + 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..2221cc5 --- /dev/null +++ b/prefs/src/test/java/org/apache/harmony/prefs/tests/java/util/prefs/PreferencesTest.java @@ -0,0 +1,1980 @@ +/* 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 dalvik.annotation.KnownFailure; +import dalvik.annotation.TestLevel; +import dalvik.annotation.TestTargetClass; +import dalvik.annotation.TestTargetNew; +import dalvik.annotation.TestTargets; + +import junit.framework.TestCase; + +import java.io.ByteArrayInputStream; +import java.io.File; +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; + +/** + * + */ +@TestTargetClass(Preferences.class) +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); + + String userHome = System.getProperty("user.home"); + if (userHome != null) { + File userHomeDir = new File(userHome); + if (!userHomeDir.isDirectory() || !userHomeDir.canWrite()) { + userHome = null; + } + } + if (userHome == null) { + System.setProperty("user.home", System.getProperty("java.io.tmpdir")); + } + + Preferences p = Preferences.userNodeForPackage(Preferences.class); + p.clear(); + try { + p.removeNode(); + } catch (BackingStoreException e) { + } + } + + /* + * @see TestCase#tearDown() + */ + protected void tearDown() throws Exception { + super.tearDown(); + stream.close(); + Preferences p = Preferences.userNodeForPackage(Preferences.class); + p.clear(); + try { + p.removeNode(); + } catch (BackingStoreException e) { + } + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "systemNodeForPackage", + args = {java.lang.Class.class} + ) + public void testSystemNodeForPackage() { + 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 + } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking missed.", + method = "systemRoot", + args = {} + ) + public void testSystemRoot() { + 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); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Checks constant values", + method = "!Constants", + args = {} + ) + public void testConsts() { + assertEquals(80, Preferences.MAX_KEY_LENGTH); + assertEquals(80, Preferences.MAX_NAME_LENGTH); + assertEquals(8192, Preferences.MAX_VALUE_LENGTH); + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking missed.", + method = "userNodeForPackage", + args = {java.lang.Class.class} + ) + 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) { + } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking missed.", + method = "userRoot", + args = {} + ) + public void testUserRoot() { + 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); + } + + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException & IOException checking missed.", + method = "importPreferences", + args = {java.io.InputStream.class} + ) + @KnownFailure("xml validation does not work") + public void testImportPreferences2() throws Exception { + InputStream 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) { + } catch (UnsupportedEncodingException e) { + } + + } + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException & IOException checking missed.", + method = "importPreferences", + args = {java.io.InputStream.class} + ) + 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-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) { + } + } + } + + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "Test for Exceptions only.", + method = "importPreferences", + args = {java.io.InputStream.class} + ) + 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) { + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking.", + method = "userRoot", + args = {} + ), + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking.", + method = "systemRoot", + args = {} + ), + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking.", + method = "userNodeForPackage", + args = {java.lang.Class.class} + ), + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking.", + method = "systemNodeForPackage", + args = {java.lang.Class.class} + ), + @TestTargetNew( + level = TestLevel.PARTIAL_COMPLETE, + notes = "SecurityException checking.", + method = "importPreferences", + args = {java.io.InputStream.class} + ) + }) + 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(); + } + } + + @TestTargets({ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "absolutePath", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "childrenNames", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "clear", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "exportNode", + args = {java.io.OutputStream.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "exportSubtree", + args = {java.io.OutputStream.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "flush", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "get", + args = {java.lang.String.class, java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "getBoolean", + args = {java.lang.String.class, boolean.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "getByteArray", + args = {java.lang.String.class, byte[].class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "getFloat", + args = {java.lang.String.class, float.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "getDouble", + args = {java.lang.String.class, double.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "getInt", + args = {java.lang.String.class, int.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "getLong", + args = {java.lang.String.class, long.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "isUserNode", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "keys", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "name", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "node", + args = {java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "nodeExists", + args = {java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "parent", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "put", + args = {java.lang.String.class, java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "putBoolean", + args = {java.lang.String.class, boolean.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "putByteArray", + args = {java.lang.String.class, byte[].class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "putDouble", + args = {java.lang.String.class, double.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "putFloat", + args = {java.lang.String.class, float.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "putInt", + args = {java.lang.String.class, int.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "putLong", + args = {java.lang.String.class, long.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "remove", + args = {java.lang.String.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "removeNode", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "addNodeChangeListener", + args = {java.util.prefs.NodeChangeListener.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "addPreferenceChangeListener", + args = {java.util.prefs.PreferenceChangeListener.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "removeNodeChangeListener", + args = {java.util.prefs.NodeChangeListener.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "removePreferenceChangeListener", + args = {java.util.prefs.PreferenceChangeListener.class} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "sync", + args = {} + ), + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Test for abstract methods.", + method = "toString", + args = {} + ) + }) + 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(); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "Preferences", + args = {} + ) + public void testConstructor() { + MockPreferences mp = new MockPreferences(); + assertEquals(mp.getClass(), MockPreferences.class); + } + + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "Check existed implementation", + method = "toString", + args = {} + ) + 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() + * + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "absolutePath", + args = {} + ) + public void testAbsolutePath() { + Preferences p = Preferences.userNodeForPackage(Preferences.class); + assertEquals("/java/util/prefs", p.absolutePath()); + + } + + /** + * @test java.util.prefs.Preferences#childrenNames() + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "childrenNames", + args = {} + ) + 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(3, pref.childrenNames().length); + assertEquals("child1", pref.childrenNames()[0]); + assertEquals(1, child1.childrenNames().length); + assertEquals("subchild1", child1.childrenNames()[0]); + } + + /** + * @test java.util.prefs.Preferences#clear() + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "clear", + args = {} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "get", + args = {java.lang.String.class, java.lang.String.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "getBoolean", + args = {java.lang.String.class, boolean.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "getByteArray", + args = {java.lang.String.class, byte[].class} + ) + public void testGetByteArray() { + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "getDouble", + args = {java.lang.String.class, double.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "getFloat", + args = {java.lang.String.class, float.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "getInt", + args = {java.lang.String.class, int.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "getLong", + args = {java.lang.String.class, long.class} + ) + 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() + * + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "isUserNode", + args = {} + ) + 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() + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Exceptions checking missed, but method is abstract, probably it is OK", + method = "keys", + args = {} + ) + 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() + * + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "name", + args = {} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "node", + args = {java.lang.String.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException & BackingStoreException checking missed, but method is abstract, probably it is OK", + method = "nodeExists", + args = {java.lang.String.class} + ) + 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() + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "parent", + args = {} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "put", + args = {java.lang.String.class, java.lang.String.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "putBoolean", + args = {java.lang.String.class, boolean.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "putDouble", + args = {java.lang.String.class, double.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "putFloat", + args = {java.lang.String.class, float.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "putInt", + args = {java.lang.String.class, int.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "putLong", + args = {java.lang.String.class, long.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "putByteArray", + args = {java.lang.String.class, byte[].class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.COMPLETE, + notes = "", + method = "remove", + args = {java.lang.String.class} + ) + 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); + fail(); + } catch (NullPointerException e) { + } + + pref.removeNode(); + try { + pref.remove("key"); + fail(); + } catch (IllegalStateException e) { + } + } + + /** + * @test java.util.prefs.Preferences#removeNode() + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Exceptions checking missed, but method is abstract, probably it is OK", + method = "removeNode", + args = {} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Only NullPointerException checked, but method is abstract, probably it is OK", + method = "addNodeChangeListener", + args = {java.util.prefs.NodeChangeListener.class} + ) + 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"); + nl.waitForEvent(); + assertEquals(1, nl.getAdded()); + nl.reset(); + child2 = pref.node("mock1"); + nl.waitForEvent(); + 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"); + nl.waitForEvent(); + 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(); + nl.waitForEvent(); + 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(); + nl.waitForEvent(); + 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"); + nl.waitForEvent(); + assertEquals(1, nl.getAdded()); + nl.reset(); + child3 = pref.node("mock4/mock5/mock6"); + nl.waitForEvent(); + assertEquals(0, nl.getAdded()); + nl.reset(); + + child3.removeNode(); + nl.waitForEvent(); + assertEquals(0, nl.getRemoved()); + nl.reset(); + + child3 = pref.node("mock4/mock7"); + nl.waitForEvent(); + assertEquals(1, nl.getAdded()); + nl.reset(); + + child1.removeNode(); + nl.waitForEvent(); + assertEquals(2, nl.getRemoved()); // fail 1 + nl.reset(); + } finally { + try { + child1.removeNode(); + } catch (Exception e) { + } + } + + } + + /** + * @test java.util.prefs.Preferences#addPreferenceChangeListener(PreferenceChangeListener pcl) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "Only NullPointerException checked, but method is abstract, probably it is OK", + method = "addPreferenceChangeListener", + args = {java.util.prefs.PreferenceChangeListener.class} + ) + public void testAddPreferenceChangeListener() { + + Preferences pref = Preferences.userNodeForPackage(Preferences.class); + MockPreferenceChangeListener pl = null; + + 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); + pl.waitForEvent(); + assertEquals(1, pl.getChanged()); + pref.putLong("long_key", Long.MAX_VALUE); + pl.waitForEvent(2); + assertEquals(2, pl.getChanged()); + pl.reset(); + try { + pref.clear(); + pl.waitForEvent(2); + assertEquals(2, pl.getChanged()); // fail 1 + } 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); + pl.waitForEvent(2); + 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); + pl.waitForEvent(); + assertEquals(1, pl.getChanged()); + try { + pref.clear(); + pl.waitForEvent(3); + assertEquals(3, pl.getChanged()); // fails + } 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(); + pl.waitForEvent(4); + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "removeNodeChangeListener", + args = {java.util.prefs.NodeChangeListener.class} + ) + 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) + * + */ + @TestTargetNew( + level = TestLevel.PARTIAL, + notes = "IllegalStateException checking missed, but method is abstract, probably it is OK", + method = "removePreferenceChangeListener", + args = {java.util.prefs.PreferenceChangeListener.class} + ) + 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(); + } + } + + @SuppressWarnings("unused") + 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..843d733 --- /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 = tests.TestSuiteFactory.createTestSuite("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 Binary files differnew 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 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 Binary files differnew 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 |