Source for java.util.prefs.Preferences

   1: /* Preferences -- Preference node containing key value entries and subnodes
   2:    Copyright (C) 2001, 2004, 2005, 2006  Free Software Foundation, Inc.
   3: 
   4: This file is part of GNU Classpath.
   5: 
   6: GNU Classpath is free software; you can redistribute it and/or modify
   7: it under the terms of the GNU General Public License as published by
   8: the Free Software Foundation; either version 2, or (at your option)
   9: any later version.
  10: 
  11: GNU Classpath is distributed in the hope that it will be useful, but
  12: WITHOUT ANY WARRANTY; without even the implied warranty of
  13: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14: General Public License for more details.
  15: 
  16: You should have received a copy of the GNU General Public License
  17: along with GNU Classpath; see the file COPYING.  If not, write to the
  18: Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  19: 02110-1301 USA.
  20: 
  21: Linking this library statically or dynamically with other modules is
  22: making a combined work based on this library.  Thus, the terms and
  23: conditions of the GNU General Public License cover the whole
  24: combination.
  25: 
  26: As a special exception, the copyright holders of this library give you
  27: permission to link this library with independent modules to produce an
  28: executable, regardless of the license terms of these independent
  29: modules, and to copy and distribute the resulting executable under
  30: terms of your choice, provided that you also meet, for each linked
  31: independent module, the terms and conditions of the license of that
  32: module.  An independent module is a module which is not derived from
  33: or based on this library.  If you modify this library, you may extend
  34: this exception to your version of the library, but you are not
  35: obligated to do so.  If you do not wish to do so, delete this
  36: exception statement from your version. */
  37: 
  38: package java.util.prefs;
  39: 
  40: import gnu.classpath.ServiceFactory;
  41: import gnu.java.util.prefs.NodeReader;
  42: 
  43: import java.io.IOException;
  44: import java.io.InputStream;
  45: import java.io.OutputStream;
  46: import java.security.AccessController;
  47: import java.security.Permission;
  48: import java.security.PrivilegedAction;
  49: import java.util.Iterator;
  50: 
  51: /**
  52:  * Preference node containing key value entries and subnodes.
  53:  * <p>
  54:  * There are two preference node trees, a system tree which can be accessed
  55:  * by calling <code>systemRoot()</code> containing system preferences usefull
  56:  * for all users, and a user tree that can be accessed by calling
  57:  * <code>userRoot()</code> containing preferences that can differ between
  58:  * different users. How different users are identified is implementation
  59:  * depended. It can be determined by Thread, Access Control Context or Subject.
  60:  * <p>
  61:  * This implementation uses the "java.util.prefs.PreferencesFactory" system
  62:  * property to find a class that implement <code>PreferencesFactory</code>
  63:  * and initialized that class (if it has a public no arguments contructor)
  64:  * to get at the actual system or user root. If the system property is not set,
  65:  * or the class cannot be initialized it uses the default implementation
  66:  * <code>gnu.java.util.prefs.FileBasedFactory</code>.
  67:  * <p>
  68:  * Besides the two static method above to get the roots of the system and user
  69:  * preference node trees there are also two convenience methods to access the
  70:  * default preference node for a particular package an object is in. These are
  71:  * <code>userNodeForPackage()</code> and <code>systemNodeForPackage()</code>.
  72:  * Both methods take an Object as an argument so accessing preferences values
  73:  * can be as easy as calling <code>Preferences.userNodeForPackage(this)</code>.
  74:  * <p>
  75:  * Note that if a security manager is installed all static methods check for
  76:  * <code>RuntimePermission("preferences")</code>. But if this permission is
  77:  * given to the code then it can access and change all (user) preference nodes
  78:  * and entries. So you should be carefull not to store to sensitive information
  79:  * or make security decissions based on preference values since there is no
  80:  * more fine grained control over what preference values can be changed once
  81:  * code has been given the correct runtime permission.
  82:  * <p>
  83:  * XXX
  84:  *
  85:  * @since 1.4
  86:  * @author Mark Wielaard (mark@klomp.org)
  87:  */
  88: public abstract class Preferences {
  89: 
  90:     // Static Fields
  91: 
  92:     /**
  93:      * Default PreferencesFactory class used when the system property
  94:      * "java.util.prefs.PreferencesFactory" is not set.
  95:      */
  96:     private static final String defaultFactoryClass
  97:         = "gnu.java.util.prefs.FileBasedFactory";
  98: 
  99:     /** Permission needed to access system or user root. */
 100:     private static final Permission prefsPermission
 101:         = new RuntimePermission("preferences");
 102: 
 103:     /**
 104:      * The preferences factory object that supplies the system and user root.
 105:      * Set and returned by the getFactory() method.
 106:      */
 107:     private static PreferencesFactory factory;
 108: 
 109:     /** Maximum node name length. 80 characters. */
 110:     public static final int MAX_NAME_LENGTH = 80;
 111: 
 112:     /** Maximum entry key length. 80 characters. */
 113:     public static final int MAX_KEY_LENGTH = 80;
 114: 
 115:     /** Maximum entry value length. 8192 characters. */
 116:     public static final int MAX_VALUE_LENGTH = 8192;
 117: 
 118:     // Constructors
 119: 
 120:     /**
 121:      * Creates a new Preferences node. Can only be used by subclasses.
 122:      * Empty implementation.
 123:      */
 124:     protected Preferences() {}
 125: 
 126:     // Static methods
 127: 
 128:     /**
 129:      * Returns the system preferences root node containing usefull preferences
 130:      * for all users. It is save to cache this value since it should always
 131:      * return the same preference node.
 132:      *
 133:      * @return the root system preference node
 134:      * @exception SecurityException when a security manager is installed and
 135:      * the caller does not have <code>RuntimePermission("preferences")</code>.
 136:      */
 137:     public static Preferences systemRoot() throws SecurityException {
 138:         // Get the preferences factory and check for permission
 139:         PreferencesFactory factory = getFactory();
 140: 
 141:         return factory.systemRoot();
 142:     }
 143: 
 144:     /**
 145:      * Returns the user preferences root node containing preferences for the
 146:      * the current user. How different users are identified is implementation
 147:      * depended. It can be determined by Thread, Access Control Context or
 148:      * Subject.
 149:      *
 150:      * @return the root user preference node
 151:      * @exception SecurityException when a security manager is installed and
 152:      * the caller does not have <code>RuntimePermission("preferences")</code>.
 153:      */
 154:     public static Preferences userRoot() throws SecurityException {
 155:         // Get the preferences factory and check for permission
 156:         PreferencesFactory factory = getFactory();
 157:         return factory.userRoot();
 158:     }
 159: 
 160:     /**
 161:      * Private helper method for <code>systemRoot()</code> and
 162:      * <code>userRoot()</code>. Checks security permission and instantiates the
 163:      * correct factory if it has not yet been set.
 164:      * <p>
 165:      * When the preferences factory has not yet been set this method first
 166:      * tries to get the system propery "java.util.prefs.PreferencesFactory"
 167:      * and tries to initializes that class. If the system property is not set
 168:      * or initialization fails it returns an instance of the default factory
 169:      * <code>gnu.java.util.prefs.FileBasedPreferencesFactory</code>.
 170:      *
 171:      * @return the preferences factory to use
 172:      * @exception SecurityException when a security manager is installed and
 173:      * the caller does not have <code>RuntimePermission("preferences")</code>.
 174:      */
 175:     private static PreferencesFactory getFactory() throws SecurityException {
 176: 
 177:         // First check for permission
 178:         SecurityManager sm = System.getSecurityManager();
 179:         if (sm != null) {
 180:             sm.checkPermission(prefsPermission);
 181:         }
 182: 
 183:         // Get the factory
 184:         if (factory == null) {
 185:             // Caller might not have enough permissions
 186:             factory = AccessController.doPrivileged(
 187:                         new PrivilegedAction<PreferencesFactory>() {
 188:                             public PreferencesFactory run() {
 189:                                 PreferencesFactory pf = null;
 190:                                 String className = System.getProperty
 191:                                     ("java.util.prefs.PreferencesFactory");
 192:                                 if (className != null) {
 193:                                     try {
 194:                                         Class fc = Class.forName(className);
 195:                                         Object o = fc.newInstance();
 196:                                         pf = (PreferencesFactory) o;
 197:                                     } catch (ClassNotFoundException cnfe)
 198:                                         {/*ignore*/}
 199:                                     catch (InstantiationException ie)
 200:                                         {/*ignore*/}
 201:                                     catch (IllegalAccessException iae)
 202:                                         {/*ignore*/}
 203:                                     catch (ClassCastException cce)
 204:                                         {/*ignore*/}
 205:                                 }
 206:                                 return pf;
 207:                             }
 208:                         });
 209: 
 210:             // Still no factory? Try to see if we have one defined
 211:             // as a System Preference
 212:             if (factory == null)
 213:               {
 214:                 Iterator iter = ServiceFactory.lookupProviders
 215:                     (PreferencesFactory.class, null);
 216: 
 217:                 if (iter != null && iter.hasNext())
 218:                   factory = (PreferencesFactory) iter.next();
 219:               }
 220: 
 221:             // Still no factory? Use our default.
 222:             if (factory == null)
 223:               {
 224:                 try
 225:                   {
 226:                     Class cls = Class.forName (defaultFactoryClass);
 227:                     factory = (PreferencesFactory) cls.newInstance();
 228:                   }
 229:                 catch (Exception e)
 230:                   {
 231:                     throw new RuntimeException ("Couldn't load default factory"
 232:                         + " '"+ defaultFactoryClass +"'", e);
 233:                   }
 234:               }
 235: 
 236:         }
 237: 
 238:         return factory;
 239:     }
 240: 
 241:     /**
 242:      * Returns the system preferences node for the package of a class.
 243:      * The package node name of the class is determined by dropping the
 244:      * final component of the fully qualified class name and
 245:      * changing all '.' to '/' in the package name. If the class of the
 246:      * object has no package then the package node name is "&lt;unnamed&gt;".
 247:      * The returned node is <code>systemRoot().node(packageNodeName)</code>.
 248:      *
 249:      * @param c Object whose default system preference node is requested
 250:      * @returns system preferences node that should be used by class c
 251:      * @exception SecurityException when a security manager is installed and
 252:      * the caller does not have <code>RuntimePermission("preferences")</code>.
 253:      */
 254:     public static Preferences systemNodeForPackage(Class<?> c)
 255:             throws SecurityException
 256:     {
 257:         return nodeForPackage(c, systemRoot());
 258:     }
 259: 
 260:     /**
 261:      * Returns the user preferences node for the package of a class.
 262:      * The package node name of the class is determined by dropping the
 263:      * final component of the fully qualified class name and
 264:      * changing all '.' to '/' in the package name. If the class of the
 265:      * object has no package then the package node name is "&lt;unnamed&gt;".
 266:      * The returned node is <code>userRoot().node(packageNodeName)</code>.
 267:      *
 268:      * @param c Object whose default userpreference node is requested
 269:      * @returns userpreferences node that should be used by class c
 270:      * @exception SecurityException when a security manager is installed and
 271:      * the caller does not have <code>RuntimePermission("preferences")</code>.
 272:      */
 273:     public static Preferences userNodeForPackage(Class<?> c)
 274:             throws SecurityException
 275:     {
 276:         return nodeForPackage(c, userRoot());
 277:     }
 278: 
 279:     /**
 280:      * Private helper method for <code>systemNodeForPackage()</code> and
 281:      * <code>userNodeForPackage()</code>. Given the correct system or user
 282:      * root it returns the correct Preference node for the package node name
 283:      * of the given object.
 284:      */
 285:     private static Preferences nodeForPackage(Class c, Preferences root) {
 286:         // Get the package path
 287:         String className = c.getName();
 288:         String packagePath;
 289:         int index = className.lastIndexOf('.');
 290:         if(index == -1) {
 291:             packagePath = "<unnamed>";
 292:         } else {
 293:             packagePath = className.substring(0,index).replace('.','/');
 294:         }
 295: 
 296:         return root.node(packagePath);
 297:     }
 298: 
 299:     /**
 300:      * Import preferences from the given input stream.  This expects
 301:      * preferences to be represented in XML as emitted by
 302:      * {@link #exportNode(OutputStream)} and
 303:      * {@link #exportSubtree(OutputStream)}.
 304:      * @throws IOException if there is an error while reading
 305:      * @throws InvalidPreferencesFormatException if the XML is not properly
 306:      * formatted
 307:      */
 308:     public static void importPreferences(InputStream is)
 309:                                     throws InvalidPreferencesFormatException,
 310:                                            IOException
 311:     {
 312:         PreferencesFactory factory = getFactory();
 313:         NodeReader reader = new NodeReader(is, factory);
 314:         reader.importPreferences();
 315:     }
 316: 
 317:     // abstract methods (identification)
 318: 
 319:     /**
 320:      * Returns the absolute path name of this preference node.
 321:      * The absolute path name of a node is the path name of its parent node
 322:      * plus a '/' plus its own name. If the node is the root node and has no
 323:      * parent then its name is "" and its absolute path name is "/".
 324:      */
 325:     public abstract String absolutePath();
 326: 
 327:     /**
 328:      * Returns true if this node comes from the user preferences tree, false
 329:      * if it comes from the system preferences tree.
 330:      */
 331:     public abstract boolean isUserNode();
 332: 
 333:     /**
 334:      * Returns the name of this preferences node. The name of the node cannot
 335:      * be null, can be mostly 80 characters and cannot contain any '/'
 336:      * characters. The root node has as name "".
 337:      */
 338:     public abstract String name();
 339: 
 340:     /**
 341:      * Returns the String given by
 342:      * <code>
 343:      * (isUserNode() ? "User":"System") + " Preference Node: " + absolutePath()
 344:      * </code>
 345:      */
 346:     public abstract String toString();
 347: 
 348:     // abstract methods (navigation)
 349: 
 350:     /**
 351:      * Returns all the direct sub nodes of this preferences node.
 352:      * Needs access to the backing store to give a meaningfull answer.
 353:      *
 354:      * @exception BackingStoreException when the backing store cannot be
 355:      *            reached
 356:      * @exception IllegalStateException when this node has been removed
 357:      */
 358:     public abstract String[] childrenNames() throws BackingStoreException;
 359: 
 360:     /**
 361:      * Returns a sub node of this preferences node if the given path is
 362:      * relative (does not start with a '/') or a sub node of the root
 363:      * if the path is absolute (does start with a '/').
 364:      *
 365:      * @exception IllegalStateException if this node has been removed
 366:      * @exception IllegalArgumentException if the path contains two or more
 367:      * consecutive '/' characters, ends with a '/' charactor and is not the
 368:      * string "/" (indicating the root node) or any name on the path is more
 369:      * then 80 characters long
 370:      */
 371:     public abstract Preferences node(String path);
 372: 
 373:     /**
 374:      * Returns true if the node that the path points to exists in memory or
 375:      * in the backing store. Otherwise it returns false or an exception is
 376:      * thrown. When this node is removed the only valid parameter is the
 377:      * empty string (indicating this node), the return value in that case
 378:      * will be false.
 379:      *
 380:      * @exception BackingStoreException when the backing store cannot be
 381:      *            reached
 382:      * @exception IllegalStateException if this node has been removed
 383:      *            and the path is not the empty string (indicating this node)
 384:      * @exception IllegalArgumentException if the path contains two or more
 385:      * consecutive '/' characters, ends with a '/' charactor and is not the
 386:      * string "/" (indicating the root node) or any name on the path is more
 387:      * then 80 characters long
 388:      */
 389:     public abstract boolean nodeExists(String path)
 390:                                 throws BackingStoreException;
 391: 
 392:     /**
 393:      * Returns the parent preferences node of this node or null if this is
 394:      * the root of the preferences tree.
 395:      *
 396:      * @exception IllegalStateException if this node has been removed
 397:      */
 398:     public abstract Preferences parent();
 399: 
 400:     // abstract methods (export)
 401: 
 402:     /**
 403:      * Export this node, but not its descendants, as XML to the
 404:      * indicated output stream.  The XML will be encoded using UTF-8
 405:      * and will use a specified document type:<br>
 406:      * <code>&lt;!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"&gt;</code><br>
 407:      * @param os the output stream to which the XML is sent
 408:      * @throws BackingStoreException if preference data cannot be read
 409:      * @throws IOException if an error occurs while writing the XML
 410:      * @throws IllegalStateException if this node or an ancestor has been removed
 411:      */
 412:     public abstract void exportNode(OutputStream os)
 413:                                 throws BackingStoreException,
 414:                                        IOException;
 415: 
 416:     /**
 417:      * Export this node and all its descendants as XML to the
 418:      * indicated output stream.  The XML will be encoded using UTF-8
 419:      * and will use a specified document type:<br>
 420:      * <code>&lt;!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd"&gt;</code><br>
 421:      * @param os the output stream to which the XML is sent
 422:      * @throws BackingStoreException if preference data cannot be read
 423:      * @throws IOException if an error occurs while writing the XML
 424:      * @throws IllegalStateException if this node or an ancestor has been removed
 425:      */
 426:     public abstract void exportSubtree(OutputStream os)
 427:                                 throws BackingStoreException,
 428:                                        IOException;
 429: 
 430:     // abstract methods (preference entry manipulation)
 431: 
 432:     /**
 433:      * Returns an (possibly empty) array with all the keys of the preference
 434:      * entries of this node.
 435:      *
 436:      * @exception BackingStoreException when the backing store cannot be
 437:      *            reached
 438:      * @exception IllegalStateException if this node has been removed
 439:      */
 440:     public abstract String[] keys() throws BackingStoreException;
 441: 
 442:     /**
 443:      * Returns the value associated with the key in this preferences node. If
 444:      * the default value of the key cannot be found in the preferences node
 445:      * entries or something goes wrong with the backing store the supplied
 446:      * default value is returned.
 447:      *
 448:      * @exception IllegalArgumentException if key is larger then 80 characters
 449:      * @exception IllegalStateException if this node has been removed
 450:      * @exception NullPointerException if key is null
 451:      */
 452:     public abstract String get(String key, String defaultVal);
 453: 
 454:     /**
 455:      * Convenience method for getting the given entry as a boolean.
 456:      * When the string representation of the requested entry is either
 457:      * "true" or "false" (ignoring case) then that value is returned,
 458:      * otherwise the given default boolean value is returned.
 459:      *
 460:      * @exception IllegalArgumentException if key is larger then 80 characters
 461:      * @exception IllegalStateException if this node has been removed
 462:      * @exception NullPointerException if key is null
 463:      */
 464:     public abstract boolean getBoolean(String key, boolean defaultVal);
 465: 
 466:     /**
 467:      * Convenience method for getting the given entry as a byte array.
 468:      * When the string representation of the requested entry is a valid
 469:      * Base64 encoded string (without any other characters, such as newlines)
 470:      * then the decoded Base64 string is returned as byte array,
 471:      * otherwise the given default byte array value is returned.
 472:      *
 473:      * @exception IllegalArgumentException if key is larger then 80 characters
 474:      * @exception IllegalStateException if this node has been removed
 475:      * @exception NullPointerException if key is null
 476:      */
 477:     public abstract byte[] getByteArray(String key, byte[] defaultVal);
 478: 
 479:     /**
 480:      * Convenience method for getting the given entry as a double.
 481:      * When the string representation of the requested entry can be decoded
 482:      * with <code>Double.parseDouble()</code> then that double is returned,
 483:      * otherwise the given default double value is returned.
 484:      *
 485:      * @exception IllegalArgumentException if key is larger then 80 characters
 486:      * @exception IllegalStateException if this node has been removed
 487:      * @exception NullPointerException if key is null
 488:      */
 489:     public abstract double getDouble(String key, double defaultVal);
 490: 
 491:     /**
 492:      * Convenience method for getting the given entry as a float.
 493:      * When the string representation of the requested entry can be decoded
 494:      * with <code>Float.parseFloat()</code> then that float is returned,
 495:      * otherwise the given default float value is returned.
 496:      *
 497:      * @exception IllegalArgumentException if key is larger then 80 characters
 498:      * @exception IllegalStateException if this node has been removed
 499:      * @exception NullPointerException if key is null
 500:      */
 501:     public abstract float getFloat(String key, float defaultVal);
 502: 
 503:     /**
 504:      * Convenience method for getting the given entry as an integer.
 505:      * When the string representation of the requested entry can be decoded
 506:      * with <code>Integer.parseInt()</code> then that integer is returned,
 507:      * otherwise the given default integer value is returned.
 508:      *
 509:      * @exception IllegalArgumentException if key is larger then 80 characters
 510:      * @exception IllegalStateException if this node has been removed
 511:      * @exception NullPointerException if key is null
 512:      */
 513:     public abstract int getInt(String key, int defaultVal);
 514: 
 515:     /**
 516:      * Convenience method for getting the given entry as a long.
 517:      * When the string representation of the requested entry can be decoded
 518:      * with <code>Long.parseLong()</code> then that long is returned,
 519:      * otherwise the given default long value is returned.
 520:      *
 521:      * @exception IllegalArgumentException if key is larger then 80 characters
 522:      * @exception IllegalStateException if this node has been removed
 523:      * @exception NullPointerException if key is null
 524:      */
 525:     public abstract long getLong(String key, long defaultVal);
 526: 
 527:     /**
 528:      * Sets the value of the given preferences entry for this node.
 529:      * Key and value cannot be null, the key cannot exceed 80 characters
 530:      * and the value cannot exceed 8192 characters.
 531:      * <p>
 532:      * The result will be immediatly visible in this VM, but may not be
 533:      * immediatly written to the backing store.
 534:      *
 535:      * @exception NullPointerException if either key or value are null
 536:      * @exception IllegalArgumentException if either key or value are to large
 537:      * @exception IllegalStateException when this node has been removed
 538:      */
 539:     public abstract void put(String key, String value);
 540: 
 541:     /**
 542:      * Convenience method for setting the given entry as a boolean.
 543:      * The boolean is converted with <code>Boolean.toString(value)</code>
 544:      * and then stored in the preference entry as that string.
 545:      *
 546:      * @exception NullPointerException if key is null
 547:      * @exception IllegalArgumentException if the key length is to large
 548:      * @exception IllegalStateException when this node has been removed
 549:      */
 550:     public abstract void putBoolean(String key, boolean value);
 551: 
 552:     /**
 553:      * Convenience method for setting the given entry as an array of bytes.
 554:      * The byte array is converted to a Base64 encoded string
 555:      * and then stored in the preference entry as that string.
 556:      * <p>
 557:      * Note that a byte array encoded as a Base64 string will be about 1.3
 558:      * times larger then the original length of the byte array, which means
 559:      * that the byte array may not be larger about 6 KB.
 560:      *
 561:      * @exception NullPointerException if either key or value are null
 562:      * @exception IllegalArgumentException if either key or value are to large
 563:      * @exception IllegalStateException when this node has been removed
 564:      */
 565:     public abstract void putByteArray(String key, byte[] value);
 566: 
 567:     /**
 568:      * Convenience method for setting the given entry as a double.
 569:      * The double is converted with <code>Double.toString(double)</code>
 570:      * and then stored in the preference entry as that string.
 571:      *
 572:      * @exception NullPointerException if the key is null
 573:      * @exception IllegalArgumentException if the key length is to large
 574:      * @exception IllegalStateException when this node has been removed
 575:      */
 576:     public abstract void putDouble(String key, double value);
 577: 
 578:     /**
 579:      * Convenience method for setting the given entry as a float.
 580:      * The float is converted with <code>Float.toString(float)</code>
 581:      * and then stored in the preference entry as that string.
 582:      *
 583:      * @exception NullPointerException if the key is null
 584:      * @exception IllegalArgumentException if the key length is to large
 585:      * @exception IllegalStateException when this node has been removed
 586:      */
 587:     public abstract void putFloat(String key, float value);
 588: 
 589:     /**
 590:      * Convenience method for setting the given entry as an integer.
 591:      * The integer is converted with <code>Integer.toString(int)</code>
 592:      * and then stored in the preference entry as that string.
 593:      *
 594:      * @exception NullPointerException if the key is null
 595:      * @exception IllegalArgumentException if the key length is to large
 596:      * @exception IllegalStateException when this node has been removed
 597:      */
 598:     public abstract void putInt(String key, int value);
 599: 
 600:     /**
 601:      * Convenience method for setting the given entry as a long.
 602:      * The long is converted with <code>Long.toString(long)</code>
 603:      * and then stored in the preference entry as that string.
 604:      *
 605:      * @exception NullPointerException if the key is null
 606:      * @exception IllegalArgumentException if the key length is to large
 607:      * @exception IllegalStateException when this node has been removed
 608:      */
 609:     public abstract void putLong(String key, long value);
 610: 
 611:     /**
 612:      * Removes the preferences entry from this preferences node.
 613:      * <p>
 614:      * The result will be immediatly visible in this VM, but may not be
 615:      * immediatly written to the backing store.
 616:      *
 617:      * @exception NullPointerException if the key is null
 618:      * @exception IllegalArgumentException if the key length is to large
 619:      * @exception IllegalStateException when this node has been removed
 620:      */
 621:     public abstract void remove(String key);
 622: 
 623:     // abstract methods (preference node manipulation)
 624: 
 625:     /**
 626:      * Removes all entries from this preferences node. May need access to the
 627:      * backing store to get and clear all entries.
 628:      * <p>
 629:      * The result will be immediatly visible in this VM, but may not be
 630:      * immediatly written to the backing store.
 631:      *
 632:      * @exception BackingStoreException when the backing store cannot be
 633:      *            reached
 634:      * @exception IllegalStateException if this node has been removed
 635:      */
 636:     public abstract void clear() throws BackingStoreException;
 637: 
 638:     /**
 639:      * Writes all preference changes on this and any subnode that have not
 640:      * yet been written to the backing store. This has no effect on the
 641:      * preference entries in this VM, but it makes sure that all changes
 642:      * are visible to other programs (other VMs might need to call the
 643:      * <code>sync()</code> method to actually see the changes to the backing
 644:      * store.
 645:      *
 646:      * @exception BackingStoreException when the backing store cannot be
 647:      *            reached
 648:      * @exception IllegalStateException if this node has been removed
 649:      */
 650:     public abstract void flush() throws BackingStoreException;
 651: 
 652:     /**
 653:      * Writes and reads all preference changes to and from this and any
 654:      * subnodes. This makes sure that all local changes are written to the
 655:      * backing store and that all changes to the backing store are visible
 656:      * in this preference node (and all subnodes).
 657:      *
 658:      * @exception BackingStoreException when the backing store cannot be
 659:      *            reached
 660:      * @exception IllegalStateException if this node has been removed
 661:      */
 662:     public abstract void sync() throws BackingStoreException;
 663: 
 664:     /**
 665:      * Removes this and all subnodes from the backing store and clears all
 666:      * entries. After removal this instance will not be useable (except for
 667:      * a few methods that don't throw a <code>InvalidStateException</code>),
 668:      * even when a new node with the same path name is created this instance
 669:      * will not be usable again. The root (system or user) may never be removed.
 670:      * <p>
 671:      * Note that according to the specification an implementation may delay
 672:      * removal of the node from the backing store till the <code>flush()</code>
 673:      * method is called. But the <code>flush()</code> method may throw a
 674:      * <code>IllegalStateException</code> when the node has been removed.
 675:      * So most implementations will actually remove the node and any subnodes
 676:      * from the backing store immediatly.
 677:      *
 678:      * @exception BackingStoreException when the backing store cannot be
 679:      *            reached
 680:      * @exception IllegalStateException if this node has already been removed
 681:      * @exception UnsupportedOperationException if this is a root node
 682:      */
 683:     public abstract void removeNode() throws BackingStoreException;
 684: 
 685:     // abstract methods (listeners)
 686: 
 687:     public abstract void addNodeChangeListener(NodeChangeListener listener);
 688: 
 689:     public abstract void addPreferenceChangeListener
 690:                             (PreferenceChangeListener listener);
 691: 
 692:     public abstract void removeNodeChangeListener(NodeChangeListener listener);
 693: 
 694:     public abstract void removePreferenceChangeListener
 695:                             (PreferenceChangeListener listener);
 696: }