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