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