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