aboutsummaryrefslogtreecommitdiff
path: root/libjava/java/util/prefs/AbstractPreferences.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/java/util/prefs/AbstractPreferences.java')
-rw-r--r--libjava/java/util/prefs/AbstractPreferences.java1258
1 files changed, 0 insertions, 1258 deletions
diff --git a/libjava/java/util/prefs/AbstractPreferences.java b/libjava/java/util/prefs/AbstractPreferences.java
deleted file mode 100644
index 98e87ebc83a..00000000000
--- a/libjava/java/util/prefs/AbstractPreferences.java
+++ /dev/null
@@ -1,1258 +0,0 @@
-/* AbstractPreferences - Partial implementation of a Preference node
- Copyright (C) 2001 Free Software Foundation, Inc.
-
-This file is part of GNU Classpath.
-
-GNU Classpath is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
-
-GNU Classpath is distributed in the hope that it will be useful, but
-WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with GNU Classpath; see the file COPYING. If not, write to the
-Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-02111-1307 USA.
-
-Linking this library statically or dynamically with other modules is
-making a combined work based on this library. Thus, the terms and
-conditions of the GNU General Public License cover the whole
-combination.
-
-As a special exception, the copyright holders of this library give you
-permission to link this library with independent modules to produce an
-executable, regardless of the license terms of these independent
-modules, and to copy and distribute the resulting executable under
-terms of your choice, provided that you also meet, for each linked
-independent module, the terms and conditions of the license of that
-module. An independent module is a module which is not derived from
-or based on this library. If you modify this library, you may extend
-this exception to your version of the library, but you are not
-obligated to do so. If you do not wish to do so, delete this
-exception statement from your version. */
-
-package java.util.prefs;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import java.util.Iterator;
-import java.util.HashMap;
-import java.util.TreeSet;
-
-import gnu.java.util.prefs.NodeWriter;
-
-/**
- * Partial implementation of a Preference node.
- *
- * Methods that still need to be implemented are <code>isUserNode(), XXX
- *
- * @since 1.4
- * @author Mark Wielaard (mark@klomp.org)
- */
-public abstract class AbstractPreferences extends Preferences {
-
- // protected fields
-
- /**
- * Object used to lock this preference node. Any thread only locks nodes
- * downwards when it has the lock on the current node. No method should
- * synchronize on the lock of any of its parent nodes while holding the
- * lock on the current node.
- */
- protected final Object lock = new Object();
-
- /**
- * Set to true in the contructor if the node did not exist in the backing
- * store when this preference node object was created. Should be set in
- * the contructor of a subclass. Defaults to false. Used to fire node
- * changed events.
- */
- protected boolean newNode = false;
-
- // private fields
-
- /**
- * The parent preferences node or null when this is the root node.
- */
- private final AbstractPreferences parent;
-
- /**
- * The name of this node.
- * Only when this is a root node (parent == null) the name is empty.
- * It has a maximum of 80 characters and cannot contain any '/' characters.
- */
- private final String name;
-
- /** True when this node has been remove, false otherwise. */
- private boolean removed = false;
-
- /**
- * Holds all the child names and nodes of this node that have been
- * accessed by earlier <code>getChild()</code> or <code>childSpi()</code>
- * invocations and that have not been removed.
- */
- private HashMap childCache = new HashMap();
-
- // constructor
-
- /**
- * Creates a new AbstractPreferences node with the given parent and name.
- *
- * @param parent the parent of this node or null when this is the root node
- * @param name the name of this node, can not be null, only 80 characters
- * maximum, must be empty when parent is null and cannot
- * contain any '/' characters
- * @exception IllegalArgumentException when name is null, greater then 80
- * characters, not the empty string but parent is null or
- * contains a '/' character
- */
- protected AbstractPreferences(AbstractPreferences parent, String name) {
- if ( (name == null) // name should be given
- || (name.length() > MAX_NAME_LENGTH) // 80 characters max
- || (parent == null && name.length() != 0) // root has no name
- || (parent != null && name.length() == 0) // all other nodes do
- || (name.indexOf('/') != -1)) // must not contain '/'
- throw new IllegalArgumentException("Illegal name argument '"
- + name
- + "' (parent is "
- + parent == null ? "" : "not "
- + "null)");
- this.parent = parent;
- this.name = name;
- }
-
- // identification methods
-
- /**
- * Returns the absolute path name of this preference node.
- * The absolute path name of a node is the path name of its parent node
- * plus a '/' plus its own name. If the node is the root node and has no
- * parent then its path name is "" and its absolute path name is "/".
- */
- public String absolutePath() {
- if (parent == null)
- return "/";
- else
- return parent.path() + '/' + name;
- }
-
- /**
- * Private helper method for absolutePath. Returns the empty string for a
- * root node and otherwise the parentPath of its parent plus a '/'.
- */
- private String path() {
- if (parent == null)
- return "";
- else
- return parent.path() + '/' + name;
- }
-
- /**
- * Returns true if this node comes from the user preferences tree, false
- * if it comes from the system preferences tree.
- */
- abstract public boolean isUserNode();
-
- /**
- * Returns the name of this preferences node. The name of the node cannot
- * be null, can be mostly 80 characters and cannot contain any '/'
- * characters. The root node has as name "".
- */
- public String name() {
- return name;
- }
-
- /**
- * Returns the String given by
- * <code>
- * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath()
- * </code>
- */
- public String toString() {
- return (isUserNode() ? "User":"System")
- + " Preference Node: "
- + absolutePath();
- }
-
- /**
- * Returns all the direct sub nodes of this preferences node.
- * Needs access to the backing store to give a meaningfull answer.
- * <p>
- * This implementation locks this node, checks if the node has not yet
- * been removed and throws an <code>IllegalStateException</code> when it
- * has been. Then it creates a new <code>TreeSet</code> and adds any
- * already cached child nodes names. To get any uncached names it calls
- * <code>childrenNamesSpi()</code> and adds the result to the set. Finally
- * it calls <code>toArray()</code> on the created set. When the call to
- * <code>childrenNamesSpi</code> thows an <code>BackingStoreException</code>
- * this method will not catch that exception but propagate the exception
- * to the caller.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- * @exception IllegalStateException when this node has been removed
- */
- public String[] childrenNames() throws BackingStoreException {
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node removed");
-
- TreeSet childrenNames = new TreeSet();
-
- // First get all cached node names
- childrenNames.addAll(childCache.keySet());
-
- // Then add any others
- String names[] = childrenNamesSpi();
- for (int i = 0; i < names.length; i++) {
- childrenNames.add(names[i]);
- }
-
- // And return the array of names
- String[] children = new String[childrenNames.size()];
- childrenNames.toArray(children);
- return children;
-
- }
- }
-
- /**
- * Returns a sub node of this preferences node if the given path is
- * relative (does not start with a '/') or a sub node of the root
- * if the path is absolute (does start with a '/').
- * <p>
- * This method first locks this node and checks if the node has not been
- * removed, if it has been removed it throws an exception. Then if the
- * path is relative (does not start with a '/') it checks if the path is
- * legal (does not end with a '/' and has no consecutive '/' characters).
- * Then it recursively gets a name from the path, gets the child node
- * from the child-cache of this node or calls the <code>childSpi()</code>
- * method to create a new child sub node. This is done recursively on the
- * newly created sub node with the rest of the path till the path is empty.
- * If the path is absolute (starts with a '/') the lock on this node is
- * droped and this method is called on the root of the preferences tree
- * with as argument the complete path minus the first '/'.
- *
- * @exception IllegalStateException if this node has been removed
- * @exception IllegalArgumentException if the path contains two or more
- * consecutive '/' characters, ends with a '/' charactor and is not the
- * string "/" (indicating the root node) or any name on the path is more
- * then 80 characters long
- */
- public Preferences node(String path) {
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node removed");
-
- // Is it a relative path?
- if (!path.startsWith("/")) {
-
- // Check if it is a valid path
- if (path.indexOf("//") != -1 || path.endsWith("/"))
- throw new IllegalArgumentException(path);
-
- return getNode(path);
- }
- }
-
- // path started with a '/' so it is absolute
- // we drop the lock and start from the root (omitting the first '/')
- Preferences root = isUserNode() ? userRoot() : systemRoot();
- return root.node(path.substring(1));
-
- }
-
- /**
- * Private helper method for <code>node()</code>. Called with this node
- * locked. Returns this node when path is the empty string, if it is not
- * empty the next node name is taken from the path (all chars till the
- * next '/' or end of path string) and the node is either taken from the
- * child-cache of this node or the <code>childSpi()</code> method is called
- * on this node with the name as argument. Then this method is called
- * recursively on the just constructed child node with the rest of the
- * path.
- *
- * @param path should not end with a '/' character and should not contain
- * consecutive '/' characters
- * @exception IllegalArgumentException if path begins with a name that is
- * larger then 80 characters.
- */
- private Preferences getNode(String path) {
- // if mark is dom then goto end
-
- // Empty String "" indicates this node
- if (path.length() == 0)
- return this;
-
- // Calculate child name and rest of path
- String childName;
- String childPath;
- int nextSlash = path.indexOf('/');
- if (nextSlash == -1) {
- childName = path;
- childPath = "";
- } else {
- childName = path.substring(0, nextSlash);
- childPath = path.substring(nextSlash+1);
- }
-
- // Get the child node
- AbstractPreferences child;
- child = (AbstractPreferences)childCache.get(childName);
- if (child == null) {
-
- if (childName.length() > MAX_NAME_LENGTH)
- throw new IllegalArgumentException(childName);
-
- // Not in childCache yet so create a new sub node
- child = childSpi(childName);
- // XXX - check if node is new
- childCache.put(childName, child);
- }
-
- // Lock the child and go down
- synchronized(child.lock) {
- return child.getNode(childPath);
- }
- }
-
- /**
- * Returns true if the node that the path points to exists in memory or
- * in the backing store. Otherwise it returns false or an exception is
- * thrown. When this node is removed the only valid parameter is the
- * empty string (indicating this node), the return value in that case
- * will be false.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- * @exception IllegalStateException if this node has been removed
- * and the path is not the empty string (indicating this node)
- * @exception IllegalArgumentException if the path contains two or more
- * consecutive '/' characters, ends with a '/' charactor and is not the
- * string "/" (indicating the root node) or any name on the path is more
- * then 80 characters long
- */
- public boolean nodeExists(String path) throws BackingStoreException {
- synchronized(lock) {
- if (isRemoved() && path.length() != 0)
- throw new IllegalStateException("Node removed");
-
- // Is it a relative path?
- if (!path.startsWith("/")) {
-
- // Check if it is a valid path
- if (path.indexOf("//") != -1 || path.endsWith("/"))
- throw new IllegalArgumentException(path);
-
- return existsNode(path);
- }
- }
-
- // path started with a '/' so it is absolute
- // we drop the lock and start from the root (omitting the first '/')
- Preferences root = isUserNode() ? userRoot() : systemRoot();
- return root.nodeExists(path.substring(1));
-
- }
-
- private boolean existsNode(String path) throws BackingStoreException {
-
- // Empty String "" indicates this node
- if (path.length() == 0)
- return(!isRemoved());
-
- // Calculate child name and rest of path
- String childName;
- String childPath;
- int nextSlash = path.indexOf('/');
- if (nextSlash == -1) {
- childName = path;
- childPath = "";
- } else {
- childName = path.substring(0, nextSlash);
- childPath = path.substring(nextSlash+1);
- }
-
- // Get the child node
- AbstractPreferences child;
- child = (AbstractPreferences)childCache.get(childName);
- if (child == null) {
-
- if (childName.length() > MAX_NAME_LENGTH)
- throw new IllegalArgumentException(childName);
-
- // Not in childCache yet so create a new sub node
- child = getChild(childName);
-
- if (child == null)
- return false;
-
- childCache.put(childName, child);
- }
-
- // Lock the child and go down
- synchronized(child.lock) {
- return child.existsNode(childPath);
- }
- }
-
- /**
- * Returns the child sub node if it exists in the backing store or null
- * if it does not exist. Called (indirectly) by <code>nodeExists()</code>
- * when a child node name can not be found in the cache.
- * <p>
- * Gets the lock on this node, calls <code>childrenNamesSpi()</code> to
- * get an array of all (possibly uncached) children and compares the
- * given name with the names in the array. If the name is found in the
- * array <code>childSpi()</code> is called to get an instance, otherwise
- * null is returned.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- */
- protected AbstractPreferences getChild(String name)
- throws BackingStoreException
- {
- synchronized(lock) {
- // Get all the names (not yet in the cache)
- String[] names = childrenNamesSpi();
- for (int i=0; i < names.length; i++)
- if (name.equals(names[i]))
- return childSpi(name);
-
- // No child with that name found
- return null;
- }
- }
-
- /**
- * Returns true if this node has been removed with the
- * <code>removeNode()</code> method, false otherwise.
- * <p>
- * Gets the lock on this node and then returns a boolean field set by
- * <code>removeNode</code> methods.
- */
- protected boolean isRemoved() {
- synchronized(lock) {
- return removed;
- }
- }
-
- /**
- * Returns the parent preferences node of this node or null if this is
- * the root of the preferences tree.
- * <p>
- * Gets the lock on this node, checks that the node has not been removed
- * and returns the parent given to the constructor.
- *
- * @exception IllegalStateException if this node has been removed
- */
- public Preferences parent() {
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node removed");
-
- return parent;
- }
- }
-
- // export methods
-
- /**
- * XXX
- */
- public void exportNode(OutputStream os)
- throws BackingStoreException,
- IOException
- {
- NodeWriter nodeWriter = new NodeWriter(this, os);
- nodeWriter.writePrefs();
- }
-
- /**
- * XXX
- */
- public void exportSubtree(OutputStream os)
- throws BackingStoreException,
- IOException
- {
- NodeWriter nodeWriter = new NodeWriter(this, os);
- nodeWriter.writePrefsTree();
- }
-
- // preference entry manipulation methods
-
- /**
- * Returns an (possibly empty) array with all the keys of the preference
- * entries of this node.
- * <p>
- * This method locks this node and checks if the node has not been
- * removed, if it has been removed it throws an exception, then it returns
- * the result of calling <code>keysSpi()</code>.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- * @exception IllegalStateException if this node has been removed
- */
- public String[] keys() throws BackingStoreException {
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node removed");
-
- return keysSpi();
- }
- }
-
-
- /**
- * Returns the value associated with the key in this preferences node. If
- * the default value of the key cannot be found in the preferences node
- * entries or something goes wrong with the backing store the supplied
- * default value is returned.
- * <p>
- * Checks that key is not null and not larger then 80 characters,
- * locks this node, and checks that the node has not been removed.
- * Then it calls <code>keySpi()</code> and returns
- * the result of that method or the given default value if it returned
- * null or throwed an exception.
- *
- * @exception IllegalArgumentException if key is larger then 80 characters
- * @exception IllegalStateException if this node has been removed
- * @exception NullPointerException if key is null
- */
- public String get(String key, String defaultVal) {
- if (key.length() > MAX_KEY_LENGTH)
- throw new IllegalArgumentException(key);
-
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node removed");
-
- String value;
- try {
- value = getSpi(key);
- } catch (Throwable t) {
- value = null;
- }
-
- if (value != null) {
- return value;
- } else {
- return defaultVal;
- }
- }
- }
-
- /**
- * Convenience method for getting the given entry as a boolean.
- * When the string representation of the requested entry is either
- * "true" or "false" (ignoring case) then that value is returned,
- * otherwise the given default boolean value is returned.
- *
- * @exception IllegalArgumentException if key is larger then 80 characters
- * @exception IllegalStateException if this node has been removed
- * @exception NullPointerException if key is null
- */
- public boolean getBoolean(String key, boolean defaultVal) {
- String value = get(key, null);
-
- if ("true".equalsIgnoreCase(value))
- return true;
-
- if ("false".equalsIgnoreCase(value))
- return false;
-
- return defaultVal;
- }
-
- /**
- * Convenience method for getting the given entry as a byte array.
- * When the string representation of the requested entry is a valid
- * Base64 encoded string (without any other characters, such as newlines)
- * then the decoded Base64 string is returned as byte array,
- * otherwise the given default byte array value is returned.
- *
- * @exception IllegalArgumentException if key is larger then 80 characters
- * @exception IllegalStateException if this node has been removed
- * @exception NullPointerException if key is null
- */
- public byte[] getByteArray(String key, byte[] defaultVal) {
- String value = get(key, null);
-
- byte[] b = null;
- if (value != null) {
- b = decode64(value);
- }
-
- if (b != null)
- return b;
- else
- return defaultVal;
- }
-
- /**
- * Helper method for decoding a Base64 string as an byte array.
- * Returns null on encoding error. This method does not allow any other
- * characters present in the string then the 65 special base64 chars.
- */
- private static byte[] decode64(String s) {
- ByteArrayOutputStream bs = new ByteArrayOutputStream((s.length()/4)*3);
- char[] c = new char[s.length()];
- s.getChars(0, s.length(), c, 0);
-
- // Convert from base64 chars
- int endchar = -1;
- for(int j = 0; j < c.length && endchar == -1; j++) {
- if (c[j] >= 'A' && c[j] <= 'Z') {
- c[j] -= 'A';
- } else if (c[j] >= 'a' && c[j] <= 'z') {
- c[j] = (char) (c[j] + 26 - 'a');
- } else if (c[j] >= '0' && c[j] <= '9') {
- c[j] = (char) (c[j] + 52 - '0');
- } else if (c[j] == '+') {
- c[j] = 62;
- } else if (c[j] == '/') {
- c[j] = 63;
- } else if (c[j] == '=') {
- endchar = j;
- } else {
- return null; // encoding exception
- }
- }
-
- int remaining = endchar == -1 ? c.length : endchar;
- int i = 0;
- while (remaining > 0) {
- // Four input chars (6 bits) are decoded as three bytes as
- // 000000 001111 111122 222222
-
- byte b0 = (byte) (c[i] << 2);
- if (remaining >= 2) {
- b0 += (c[i+1] & 0x30) >> 4;
- }
- bs.write(b0);
-
- if (remaining >= 3) {
- byte b1 = (byte) ((c[i+1] & 0x0F) << 4);
- b1 += (byte) ((c[i+2] & 0x3C) >> 2);
- bs.write(b1);
- }
-
- if (remaining >= 4) {
- byte b2 = (byte) ((c[i+2] & 0x03) << 6);
- b2 += c[i+3];
- bs.write(b2);
- }
-
- i += 4;
- remaining -= 4;
- }
-
- return bs.toByteArray();
- }
-
- /**
- * Convenience method for getting the given entry as a double.
- * When the string representation of the requested entry can be decoded
- * with <code>Double.parseDouble()</code> then that double is returned,
- * otherwise the given default double value is returned.
- *
- * @exception IllegalArgumentException if key is larger then 80 characters
- * @exception IllegalStateException if this node has been removed
- * @exception NullPointerException if key is null
- */
- public double getDouble(String key, double defaultVal) {
- String value = get(key, null);
-
- if (value != null) {
- try {
- return Double.parseDouble(value);
- } catch (NumberFormatException nfe) { /* ignore */ }
- }
-
- return defaultVal;
- }
-
- /**
- * Convenience method for getting the given entry as a float.
- * When the string representation of the requested entry can be decoded
- * with <code>Float.parseFloat()</code> then that float is returned,
- * otherwise the given default float value is returned.
- *
- * @exception IllegalArgumentException if key is larger then 80 characters
- * @exception IllegalStateException if this node has been removed
- * @exception NullPointerException if key is null
- */
- public float getFloat(String key, float defaultVal) {
- String value = get(key, null);
-
- if (value != null) {
- try {
- return Float.parseFloat(value);
- } catch (NumberFormatException nfe) { /* ignore */ }
- }
-
- return defaultVal;
- }
-
- /**
- * Convenience method for getting the given entry as an integer.
- * When the string representation of the requested entry can be decoded
- * with <code>Integer.parseInt()</code> then that integer is returned,
- * otherwise the given default integer value is returned.
- *
- * @exception IllegalArgumentException if key is larger then 80 characters
- * @exception IllegalStateException if this node has been removed
- * @exception NullPointerException if key is null
- */
- public int getInt(String key, int defaultVal) {
- String value = get(key, null);
-
- if (value != null) {
- try {
- return Integer.parseInt(value);
- } catch (NumberFormatException nfe) { /* ignore */ }
- }
-
- return defaultVal;
- }
-
- /**
- * Convenience method for getting the given entry as a long.
- * When the string representation of the requested entry can be decoded
- * with <code>Long.parseLong()</code> then that long is returned,
- * otherwise the given default long value is returned.
- *
- * @exception IllegalArgumentException if key is larger then 80 characters
- * @exception IllegalStateException if this node has been removed
- * @exception NullPointerException if key is null
- */
- public long getLong(String key, long defaultVal) {
- String value = get(key, null);
-
- if (value != null) {
- try {
- return Long.parseLong(value);
- } catch (NumberFormatException nfe) { /* ignore */ }
- }
-
- return defaultVal;
- }
-
- /**
- * Sets the value of the given preferences entry for this node.
- * Key and value cannot be null, the key cannot exceed 80 characters
- * and the value cannot exceed 8192 characters.
- * <p>
- * The result will be immediatly visible in this VM, but may not be
- * immediatly written to the backing store.
- * <p>
- * Checks that key and value are valid, locks this node, and checks that
- * the node has not been removed. Then it calls <code>putSpi()</code>.
- *
- * @exception NullPointerException if either key or value are null
- * @exception IllegalArgumentException if either key or value are to large
- * @exception IllegalStateException when this node has been removed
- */
- public void put(String key, String value) {
- if (key.length() > MAX_KEY_LENGTH
- || value.length() > MAX_VALUE_LENGTH)
- throw new IllegalArgumentException("key ("
- + key.length() + ")"
- + " or value ("
- + value.length() + ")"
- + " to large");
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node removed");
-
- putSpi(key, value);
-
- // XXX - fire events
- }
-
- }
-
- /**
- * Convenience method for setting the given entry as a boolean.
- * The boolean is converted with <code>Boolean.toString(value)</code>
- * and then stored in the preference entry as that string.
- *
- * @exception NullPointerException if key is null
- * @exception IllegalArgumentException if the key length is to large
- * @exception IllegalStateException when this node has been removed
- */
- public void putBoolean(String key, boolean value) {
- put(key, String.valueOf(value));
- // XXX - Use when using 1.4 compatible Boolean
- // put(key, Boolean.toString(value));
- }
-
- /**
- * Convenience method for setting the given entry as an array of bytes.
- * The byte array is converted to a Base64 encoded string
- * and then stored in the preference entry as that string.
- * <p>
- * Note that a byte array encoded as a Base64 string will be about 1.3
- * times larger then the original length of the byte array, which means
- * that the byte array may not be larger about 6 KB.
- *
- * @exception NullPointerException if either key or value are null
- * @exception IllegalArgumentException if either key or value are to large
- * @exception IllegalStateException when this node has been removed
- */
- public void putByteArray(String key, byte[] value) {
- put(key, encode64(value));
- }
-
- /**
- * Helper method for encoding an array of bytes as a Base64 String.
- */
- private static String encode64(byte[] b) {
- StringBuffer sb = new StringBuffer((b.length/3)*4);
-
- int i = 0;
- int remaining = b.length;
- char c[] = new char[4];
- while (remaining > 0) {
- // Three input bytes are encoded as four chars (6 bits) as
- // 00000011 11112222 22333333
-
- c[0] = (char) ((b[i] & 0xFC) >> 2);
- c[1] = (char) ((b[i] & 0x03) << 4);
- if (remaining >= 2) {
- c[1] += (char) ((b[i+1] & 0xF0) >> 4);
- c[2] = (char) ((b[i+1] & 0x0F) << 2);
- if (remaining >= 3) {
- c[2] += (char) ((b[i+2] & 0xC0) >> 6);
- c[3] = (char) (b[i+2] & 0x3F);
- } else {
- c[3] = 64;
- }
- } else {
- c[2] = 64;
- c[3] = 64;
- }
-
- // Convert to base64 chars
- for(int j = 0; j < 4; j++) {
- if (c[j] < 26) {
- c[j] += 'A';
- } else if (c[j] < 52) {
- c[j] = (char) (c[j] - 26 + 'a');
- } else if (c[j] < 62) {
- c[j] = (char) (c[j] - 52 + '0');
- } else if (c[j] == 62) {
- c[j] = '+';
- } else if (c[j] == 63) {
- c[j] = '/';
- } else {
- c[j] = '=';
- }
- }
-
- sb.append(c);
- i += 3;
- remaining -= 3;
- }
-
- return sb.toString();
- }
-
- /**
- * Convenience method for setting the given entry as a double.
- * The double is converted with <code>Double.toString(double)</code>
- * and then stored in the preference entry as that string.
- *
- * @exception NullPointerException if the key is null
- * @exception IllegalArgumentException if the key length is to large
- * @exception IllegalStateException when this node has been removed
- */
- public void putDouble(String key, double value) {
- put(key, Double.toString(value));
- }
-
- /**
- * Convenience method for setting the given entry as a float.
- * The float is converted with <code>Float.toString(float)</code>
- * and then stored in the preference entry as that string.
- *
- * @exception NullPointerException if the key is null
- * @exception IllegalArgumentException if the key length is to large
- * @exception IllegalStateException when this node has been removed
- */
- public void putFloat(String key, float value) {
- put(key, Float.toString(value));
- }
-
- /**
- * Convenience method for setting the given entry as an integer.
- * The integer is converted with <code>Integer.toString(int)</code>
- * and then stored in the preference entry as that string.
- *
- * @exception NullPointerException if the key is null
- * @exception IllegalArgumentException if the key length is to large
- * @exception IllegalStateException when this node has been removed
- */
- public void putInt(String key, int value) {
- put(key, Integer.toString(value));
- }
-
- /**
- * Convenience method for setting the given entry as a long.
- * The long is converted with <code>Long.toString(long)</code>
- * and then stored in the preference entry as that string.
- *
- * @exception NullPointerException if the key is null
- * @exception IllegalArgumentException if the key length is to large
- * @exception IllegalStateException when this node has been removed
- */
- public void putLong(String key, long value) {
- put(key, Long.toString(value));
- }
-
- /**
- * Removes the preferences entry from this preferences node.
- * <p>
- * The result will be immediatly visible in this VM, but may not be
- * immediatly written to the backing store.
- * <p>
- * This implementation checks that the key is not larger then 80
- * characters, gets the lock of this node, checks that the node has
- * not been removed and calls <code>removeSpi</code> with the given key.
- *
- * @exception NullPointerException if the key is null
- * @exception IllegalArgumentException if the key length is to large
- * @exception IllegalStateException when this node has been removed
- */
- public void remove(String key) {
- if (key.length() > MAX_KEY_LENGTH)
- throw new IllegalArgumentException(key);
-
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node removed");
-
- removeSpi(key);
- }
- }
-
- /**
- * Removes all entries from this preferences node. May need access to the
- * backing store to get and clear all entries.
- * <p>
- * The result will be immediatly visible in this VM, but may not be
- * immediatly written to the backing store.
- * <p>
- * This implementation locks this node, checks that the node has not been
- * removed and calls <code>keys()</code> to get a complete array of keys
- * for this node. For every key found <code>removeSpi()</code> is called.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- * @exception IllegalStateException if this node has been removed
- */
- public void clear() throws BackingStoreException {
- synchronized(lock) {
- if (isRemoved())
- throw new IllegalStateException("Node Removed");
-
- String[] keys = keys();
- for (int i = 0; i < keys.length; i++) {
- removeSpi(keys[i]);
- }
- }
- }
-
- /**
- * Writes all preference changes on this and any subnode that have not
- * yet been written to the backing store. This has no effect on the
- * preference entries in this VM, but it makes sure that all changes
- * are visible to other programs (other VMs might need to call the
- * <code>sync()</code> method to actually see the changes to the backing
- * store.
- * <p>
- * Locks this node, calls the <code>flushSpi()</code> method, gets all
- * the (cached - already existing in this VM) subnodes and then calls
- * <code>flushSpi()</code> on every subnode with this node unlocked and
- * only that particular subnode locked.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- */
- public void flush() throws BackingStoreException {
- flushNode(false);
- }
-
- /**
- * Writes and reads all preference changes to and from this and any
- * subnodes. This makes sure that all local changes are written to the
- * backing store and that all changes to the backing store are visible
- * in this preference node (and all subnodes).
- * <p>
- * Checks that this node is not removed, locks this node, calls the
- * <code>syncSpi()</code> method, gets all the subnodes and then calls
- * <code>syncSpi()</code> on every subnode with this node unlocked and
- * only that particular subnode locked.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- * @exception IllegalStateException if this node has been removed
- */
- public void sync() throws BackingStoreException {
- flushNode(true);
- }
-
-
- /**
- * Private helper method that locks this node and calls either
- * <code>flushSpi()</code> if <code>sync</code> is false, or
- * <code>flushSpi()</code> if <code>sync</code> is true. Then it gets all
- * the currently cached subnodes. For every subnode it calls this method
- * recursively with this node no longer locked.
- * <p>
- * Called by either <code>flush()</code> or <code>sync()</code>
- */
- private void flushNode(boolean sync) throws BackingStoreException {
- String[] keys = null;
- synchronized(lock) {
- if (sync) {
- syncSpi();
- } else {
- flushSpi();
- }
- keys = (String[]) childCache.keySet().toArray();
- }
-
- if (keys != null) {
- for (int i = 0; i < keys.length; i++) {
- // Have to lock this node again to access the childCache
- AbstractPreferences subNode;
- synchronized(this) {
- subNode = (AbstractPreferences) childCache.get(keys[i]);
- }
-
- // The child could already have been removed from the cache
- if (subNode != null) {
- subNode.flushNode(sync);
- }
- }
- }
- }
-
- /**
- * Removes this and all subnodes from the backing store and clears all
- * entries. After removal this instance will not be useable (except for
- * a few methods that don't throw a <code>InvalidStateException</code>),
- * even when a new node with the same path name is created this instance
- * will not be usable again.
- * <p>
- * Checks that this is not a root node. If not it locks the parent node,
- * then locks this node and checks that the node has not yet been removed.
- * Then it makes sure that all subnodes of this node are in the child cache,
- * by calling <code>childSpi()</code> on any children not yet in the cache.
- * Then for all children it locks the subnode and removes it. After all
- * subnodes have been purged the child cache is cleared, this nodes removed
- * flag is set and any listeners are called. Finally this node is removed
- * from the child cache of the parent node.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- * @exception IllegalStateException if this node has already been removed
- * @exception UnsupportedOperationException if this is a root node
- */
- public void removeNode() throws BackingStoreException {
- // Check if it is a root node
- if (parent == null)
- throw new UnsupportedOperationException("Cannot remove root node");
-
- synchronized(parent) {
- synchronized(this) {
- if (isRemoved())
- throw new IllegalStateException("Node Removed");
-
- purge();
- }
- parent.childCache.remove(name);
- }
- }
-
- /**
- * Private helper method used to completely remove this node.
- * Called by <code>removeNode</code> with the parent node and this node
- * locked.
- * <p>
- * Makes sure that all subnodes of this node are in the child cache,
- * by calling <code>childSpi()</code> on any children not yet in the
- * cache. Then for all children it locks the subnode and calls this method
- * on that node. After all subnodes have been purged the child cache is
- * cleared, this nodes removed flag is set and any listeners are called.
- */
- private void purge() throws BackingStoreException
- {
- // Make sure all children have an AbstractPreferences node in cache
- String children[] = childrenNamesSpi();
- for (int i = 0; i < children.length; i++) {
- if (childCache.get(children[i]) == null)
- childCache.put(children[i], childSpi(children[i]));
- }
-
- // purge all children
- Iterator i = childCache.values().iterator();
- while (i.hasNext()) {
- AbstractPreferences node = (AbstractPreferences) i.next();
- synchronized(node) {
- node.purge();
- }
- }
-
- // Cache is empty now
- childCache.clear();
-
- // remove this node
- removeNodeSpi();
- removed = true;
-
- // XXX - check for listeners
- }
-
- // listener methods
-
- /**
- * XXX
- */
- public void addNodeChangeListener(NodeChangeListener listener) {
- // XXX
- }
-
- public void addPreferenceChangeListener(PreferenceChangeListener listener) {
- // XXX
- }
-
- public void removeNodeChangeListener(NodeChangeListener listener) {
- // XXX
- }
-
- public void removePreferenceChangeListener
- (PreferenceChangeListener listener)
- {
- // XXX
- }
-
- // abstract spi methods
-
- /**
- * Returns the names of the sub nodes of this preference node.
- * This method only has to return any not yet cached child names,
- * but may return all names if that is easier. It must not return
- * null when there are no children, it has to return an empty array
- * in that case. Since this method must consult the backing store to
- * get all the sub node names it may throw a BackingStoreException.
- * <p>
- * Called by <code>childrenNames()</code> with this node locked.
- */
- protected abstract String[] childrenNamesSpi() throws BackingStoreException;
-
- /**
- * Returns a child note with the given name.
- * This method is called by the <code>node()</code> method (indirectly
- * through the <code>getNode()</code> helper method) with this node locked
- * if a sub node with this name does not already exist in the child cache.
- * If the child node did not aleady exist in the backing store the boolean
- * field <code>newNode</code> of the returned node should be set.
- * <p>
- * Note that this method should even return a non-null child node if the
- * backing store is not available since it may not throw a
- * <code>BackingStoreException</code>.
- */
- protected abstract AbstractPreferences childSpi(String name);
-
- /**
- * Returns an (possibly empty) array with all the keys of the preference
- * entries of this node.
- * <p>
- * Called by <code>keys()</code> with this node locked if this node has
- * not been removed. May throw an exception when the backing store cannot
- * be accessed.
- *
- * @exception BackingStoreException when the backing store cannot be
- * reached
- */
- abstract protected String[] keysSpi() throws BackingStoreException;
-
- /**
- * Returns the value associated with the key in this preferences node or
- * null when the key does not exist in this preferences node.
- * <p>
- * Called by <code>key()</code> with this node locked after checking that
- * key is valid, not null and that the node has not been removed.
- * <code>key()</code> will catch any exceptions that this method throws.
- */
- abstract protected String getSpi(String key);
-
- /**
- * Sets the value of the given preferences entry for this node.
- * The implementation is not required to propagate the change to the
- * backing store immediatly. It may not throw an exception when it tries
- * to write to the backing store and that operation fails, the failure
- * should be registered so a later invocation of <code>flush()</code>
- * or <code>sync()</code> can signal the failure.
- * <p>
- * Called by <code>put()</code> with this node locked after checking that
- * key and value are valid and non-null.
- */
- abstract protected void putSpi(String key, String value);
-
- /**
- * Removes the given key entry from this preferences node.
- * The implementation is not required to propagate the change to the
- * backing store immediatly. It may not throw an exception when it tries
- * to write to the backing store and that operation fails, the failure
- * should be registered so a later invocation of <code>flush()</code>
- * or <code>sync()</code> can signal the failure.
- * <p>
- * Called by <code>remove()</code> with this node locked after checking
- * that the key is valid and non-null.
- */
- abstract protected void removeSpi(String key);
-
- /**
- * Writes all entries of this preferences node that have not yet been
- * written to the backing store and possibly creates this node in the
- * backing store, if it does not yet exist. Should only write changes to
- * this node and not write changes to any subnodes.
- * Note that the node can be already removed in this VM. To check if
- * that is the case the implementation can call <code>isRemoved()</code>.
- * <p>
- * Called (indirectly) by <code>flush()</code> with this node locked.
- */
- abstract protected void flushSpi() throws BackingStoreException;
-
- /**
- * Writes all entries of this preferences node that have not yet been
- * written to the backing store and reads any entries that have changed
- * in the backing store but that are not yet visible in this VM.
- * Should only sync this node and not change any of the subnodes.
- * Note that the node can be already removed in this VM. To check if
- * that is the case the implementation can call <code>isRemoved()</code>.
- * <p>
- * Called (indirectly) by <code>sync()</code> with this node locked.
- */
- abstract protected void syncSpi() throws BackingStoreException;
-
- /**
- * Clears this node from this VM and removes it from the backing store.
- * After this method has been called the node is marked as removed.
- * <p>
- * Called (indirectly) by <code>removeNode()</code> with this node locked
- * after all the sub nodes of this node have already been removed.
- */
- abstract protected void removeNodeSpi() throws BackingStoreException;
-}