View Javadoc
1 package org.masukomi.prefs; 2 3 import java.io.*; 4 import java.util.prefs.BackingStoreException; 5 import java.util.prefs.NodeChangeListener; 6 import java.util.prefs.PreferenceChangeListener; 7 import java.util.prefs.InvalidPreferencesFormatException; 8 import java.util.*; 9 import org.masukomi.tools.utils.HashtableOfHashtables; 10 11 import javax.xml.parsers.*; 12 import org.xml.sax.*; 13 import org.w3c.dom.*; 14 import org.apache.crimson.tree.*; 15 16 17 /*** 18 * XMLClassPreferences is conceptually similar to java.util.prefs.Preferences 19 * @link{http://java.sun.com/j2se/1.4/docs/api/java/util/prefs/Preferences.html} 20 * the primary difference being that XMLClassPreferences stores preferences for 21 * a particular class where Preferences would store preferences for the classes 22 * package. 23 * <p> This software is copyright 2002 Kate Rhodes. <br> It is distributed under 24 * the MIT License which can be found at <a href="http://www.opensource. 25 * org/licenses/mit-license.php">http://www.opensource.org/licenses/mit-license. 26 * php</a><br> Details on this package can be found at <a href="http://tools. 27 * masukomi.org/">http://tools.masukomi.org/<;/a><br> e-mail <tt>masukomi at 28 * masukomi dot org</tt> for questions or details 29 * </p> 30 31 * @author Kate Rhodes (e-mail masukomi at masukomi dot org) 32 * 33 */ 34 public class XMLClassPreferences { 35 36 /////////////////////////////////////////////////////// 37 // Taken from java.util.prefs.XmlSupport; 38 39 // The required DTD URI for exported preferences 40 private static final String PREFS_DTD_URI = 41 "http://java.sun.com/dtd/preferences.dtd"; 42 43 // The actual DTD corresponding to the URI 44 private static final String PREFS_DTD = 45 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" 46 + "<!-- DTD for preferences -->" 47 + "<!ELEMENT preferences (root) >" 48 + "<!ATTLIST preferences" 49 + " EXTERNAL_XML_VERSION CDATA \"0.0\" >" 50 + "<!ELEMENT root (map, node*) >" 51 + "<!ATTLIST root" 52 + " type (system|user) #REQUIRED >" 53 + "<!ELEMENT node (map, node*) >" 54 + "<!ATTLIST node" 55 + " name CDATA #REQUIRED >" 56 + "<!ELEMENT map (entry*) >" 57 + "<!ATTLIST map" 58 + " MAP_XML_VERSION CDATA \"0.0\" >" 59 + "<!ELEMENT entry EMPTY >" 60 + "<!ATTLIST entry" 61 + " key CDATA #REQUIRED" 62 + " value CDATA #REQUIRED >"; 63 /*** 64 * Version number for the format exported preferences files. 65 */ 66 private static final String EXTERNAL_XML_VERSION = "1.0"; 67 68 /* 69 * Version number for the internal map files. 70 */ 71 private static final String MAP_XML_VERSION = "1.0"; 72 // END stuff from sun 73 ////////////////////////////////////////////////////////// 74 75 /*** a hashtable with the results of nodeName(Class) as the keys and an 76 * XMLClassPreferences object as the values. 77 */ 78 static Hashtable cachedSystemPrefs = new Hashtable(); 79 80 /*** a hashtable with the username as the root key it's corresponding value containing a hash with 81 * the results of nodeName(Class) as the keys and an XMLClassPreferences object as 82 * the values. 83 */ 84 static HashtableOfHashtables cachedUserPrefs = new HashtableOfHashtables(); 85 86 private String className; 87 private Hashtable internalPrefs; 88 private boolean isUserNode; 89 /*** 90 * Constructor for XMLClassPreferences ONLY 91 */ 92 public XMLClassPreferences() { 93 super(); 94 95 } 96 /*** 97 * Constructor for XMLClassPreferences. 98 */ 99 public XMLClassPreferences(String className, boolean isUserNode) { 100 //super(); 101 this.isUserNode = isUserNode; 102 this.className = className; 103 104 if (isUserNode){ 105 if (! cachedUserPrefs.containsKey(System.getProperty("user.name"), className)) { 106 cachedUserPrefs.put(System.getProperty("user.name"), className, new Hashtable()); 107 } 108 internalPrefs = cachedUserPrefs.getHashtable(System.getProperty("user.name"), className); 109 } else { 110 if (! cachedSystemPrefs.containsKey(className)) { 111 cachedSystemPrefs.put(className, new Hashtable()); 112 } 113 114 internalPrefs = (Hashtable)cachedSystemPrefs.get(className); 115 } 116 117 } 118 119 120 /*** 121 * Returns the absolute path name of the XML File corresponding to the 122 * specified Preferences object. This does not validate the existence of 123 * the file, only tells you where it should be. 124 * <p>Ex. /<prefsRoot>/<package name>/<package name>/< 125 * class name>.xml</p> 126 * 127 * @param p The Preferences node you wish to find the local representation 128 * of. 129 * 130 * @return an absolute path to the XML File corresponding to the specified 131 * Preferences object. 132 */ 133 public static String getLocalXMLFilePath( 134 String className, 135 boolean isUserNode) { 136 if (className == null) { 137 return null; 138 } 139 140 StringBuffer fileNameBuffer = 141 new StringBuffer(XMLPreferences.getPreferencesRoot()); 142 fileNameBuffer.append(System.getProperty("file.separator")); 143 if (isUserNode) { 144 fileNameBuffer.append("user"); 145 fileNameBuffer.append(System.getProperty("file.separator")); 146 fileNameBuffer.append(System.getProperty("user.name")); 147 } else { 148 fileNameBuffer.append("system"); 149 } 150 fileNameBuffer.append(System.getProperty("file.separator")); 151 152 String subPath = className; 153 154 String fileSeperator = System.getProperty("file.separator"); 155 if (fileSeperator.equals("//")){ // damn windows backslashes 156 fileSeperator= "////"; 157 } 158 159 subPath = subPath.replaceAll("//.", fileSeperator); 160 fileNameBuffer.append(subPath); 161 fileNameBuffer.append(".xml"); 162 return fileNameBuffer.toString(); 163 } 164 165 166 public static String getLocalXMLFileDir(String className, boolean isUserNode){ 167 String filePath = getLocalXMLFilePath(className, isUserNode); 168 169 filePath = filePath.substring(0,filePath.lastIndexOf(System.getProperty("file.separator"))); 170 return filePath; 171 } 172 173 /*** returns the name of the class this XMLClassPreferences object represents 174 * @return String the name of the class this XMLClassPreferences object represents 175 */ 176 public String getClassName(){ 177 return className; 178 } 179 180 181 182 /*** returns true if the key exists */ 183 public boolean containsKey(String key){ 184 return internalPrefs.containsKey(key); 185 } 186 187 /***returns true if one of the keys is mapped to the specified value 188 * @param value the value to test for the presence of 189 * @return true if one of the keys is mapped to the specified value 190 */ 191 public boolean containsValue(String value){ 192 return internalPrefs.containsValue(value); 193 } 194 195 /*** returns true if there are no values in this XMLClassPreferences 196 * 197 * @return true if there are no values in this XMLClassPreferences 198 */ 199 public boolean isEmpty(){ 200 return internalPrefs.isEmpty(); 201 } 202 /*** 203 * @see java.util.prefs.Preferences#put(java.lang.String, java.lang.String) 204 */ 205 public void put(String arg0, String arg1) { 206 if (arg0 != null){ 207 internalPrefs.put(arg0, arg1); 208 //System.out.println("XMLClassPrefs.put: just put " + arg0 + " = " + arg1); 209 } 210 } 211 212 /*** 213 * @see java.util.prefs.Preferences#get(java.lang.String, java.lang.String) 214 */ 215 public String get(String arg0, String arg1) { 216 try { 217 return internalPrefs.get(arg0).toString(); 218 } catch (NullPointerException e) { 219 return arg1; 220 } 221 } 222 223 /*** 224 * @see java.util.prefs.Preferences#remove(java.lang.String) 225 */ 226 public void remove(String arg0) { 227 internalPrefs.remove(arg0); 228 } 229 230 /*** 231 * @see java.util.prefs.Preferences#clear() 232 */ 233 public void clear() throws BackingStoreException { 234 internalPrefs.clear(); 235 } 236 237 /*** 238 * @see java.util.prefs.Preferences#putInt(java.lang.String, int) 239 */ 240 public void putInt(String arg0, int arg1) { 241 if (arg0 != null){ 242 internalPrefs.put(arg0, String.valueOf(arg1)); 243 } 244 } 245 246 /*** 247 * @see java.util.prefs.Preferences#getInt(java.lang.String, int) 248 */ 249 public int getInt(String arg0, int arg1) { 250 try { 251 return Integer.parseInt((String) internalPrefs.get(arg0)); 252 } catch (Exception e) { 253 return arg1; 254 } 255 } 256 257 /*** 258 * @see java.util.prefs.Preferences#putLong(java.lang.String, long) 259 */ 260 public void putLong(String arg0, long arg1) { 261 if (arg0 != null){ 262 internalPrefs.put(arg0, String.valueOf(arg1)); 263 } 264 } 265 266 /*** 267 * @see java.util.prefs.Preferences#getLong(java.lang.String, long) 268 */ 269 public long getLong(String arg0, long arg1) { 270 try { 271 return Long.parseLong((String) internalPrefs.get(arg0)); 272 } catch (Exception e) { 273 return arg1; 274 } 275 } 276 277 /*** 278 * @see java.util.prefs.Preferences#putBoolean(java.lang.String, boolean) 279 */ 280 public void putBoolean(String arg0, boolean arg1) { 281 if (arg0 != null){ 282 internalPrefs.put(arg0, String.valueOf(arg1)); 283 } 284 } 285 286 /*** 287 * @see java.util.prefs.Preferences#getBoolean(java.lang.String, boolean) 288 */ 289 public boolean getBoolean(String arg0, boolean arg1) { 290 try { 291 return Boolean.getBoolean((String) internalPrefs.get(arg0)); 292 } catch (Exception e) { 293 return arg1; 294 } 295 } 296 297 /*** 298 * @see java.util.prefs.Preferences#putFloat(java.lang.String, float) 299 */ 300 public void putFloat(String arg0, float arg1) { 301 if (arg0 != null){ 302 internalPrefs.put(arg0, String.valueOf(arg1)); 303 } 304 } 305 306 /*** 307 * @see java.util.prefs.Preferences#getFloat(java.lang.String, float) 308 */ 309 public float getFloat(String arg0, float arg1) { 310 try { 311 return Float.parseFloat((String) internalPrefs.get(arg0)); 312 } catch (Exception e) { 313 return arg1; 314 } 315 } 316 317 /*** 318 * @see java.util.prefs.Preferences#putDouble(java.lang.String, double) 319 */ 320 public void putDouble(String arg0, double arg1) { 321 if (arg0 != null){ 322 internalPrefs.put(arg0, String.valueOf(arg1)); 323 } 324 } 325 326 /*** 327 * @see java.util.prefs.Preferences#getDouble(java.lang.String, double) 328 */ 329 public double getDouble(String arg0, double arg1) { 330 try { 331 return Double.parseDouble((String) internalPrefs.get(arg0)); 332 } catch (Exception e) { 333 return arg1; 334 } 335 } 336 337 /*** 338 * @see java.util.prefs.Preferences#putByteArray(java.lang.String, byte) 339 */ 340 public void putByteArray(String arg0, byte[] arg1) { 341 if (arg0 != null){ 342 internalPrefs.put(arg0, arg1); 343 } 344 } 345 346 /*** 347 * @see java.util.prefs.Preferences#getByteArray(java.lang.String, byte) 348 */ 349 public byte[] getByteArray(String arg0, byte[] arg1) { 350 return null; 351 } 352 353 /*** 354 * @see java.util.prefs.Preferences#keys() 355 */ 356 public String[] keys() throws BackingStoreException { 357 358 359 if (internalPrefs.size() > 0){ 360 String[] returnKeys = new String[internalPrefs.size()]; 361 Enumeration internalKeys = internalPrefs.keys(); 362 int currentKey = 0; 363 while (internalKeys.hasMoreElements()) { 364 String currentKeyName=internalKeys.nextElement().toString(); 365 returnKeys[currentKey] = currentKeyName; 366 currentKey++; 367 } 368 //for (int x =0; x <returnKeys.length; x++){ 369 // System.out.println("XMLClassPrefs.keys(): key =" + returnKeys[x]); 370 //} 371 return returnKeys; 372 } 373 return null; 374 } 375 376 /*** 377 * @see java.util.prefs.Preferences#childrenNames() 378 */ 379 public String[] childrenNames() throws BackingStoreException { 380 return null; 381 } 382 383 /*** 384 * @see java.util.prefs.Preferences#parent() 385 */ 386 public XMLClassPreferences parent() { 387 return null; 388 } 389 390 /*** 391 * @see java.util.prefs.Preferences#node(java.lang.String) 392 */ 393 public XMLClassPreferences node(String arg0) { 394 return null; 395 } 396 397 /*** 398 * @see java.util.prefs.Preferences#nodeExists(java.lang.String) 399 */ 400 public boolean nodeExists(String arg0) throws BackingStoreException { 401 File testFile = new File(getLocalXMLFilePath(className, isUserNode)); 402 return testFile.exists(); 403 404 } 405 406 /*** 407 * @see java.util.prefs.Preferences#removeNode() 408 */ 409 public void removeNode() throws BackingStoreException { 410 411 if (isUserNode) { 412 try { 413 cachedUserPrefs.get(System.getProperty("user.name")).remove( 414 className); 415 } catch (NullPointerException e) { 416 // that's ok it's already gone. 417 } 418 } else { 419 try { 420 cachedSystemPrefs.remove(className); 421 } catch (Exception e) { 422 // that's ok it's already not there. 423 } 424 } 425 File testFile = new File(getLocalXMLFilePath(className, isUserNode)); 426 testFile.delete(); 427 } 428 429 /*** 430 * @see java.util.prefs.Preferences#name() 431 */ 432 public String name() { 433 return null; 434 } 435 436 /*** 437 * @see java.util.prefs.Preferences#absolutePath() 438 */ 439 public String absolutePath() { 440 return null; 441 } 442 443 /*** imports the local XML file for this class. If no File is found an 444 * empty XMLClassPreferences will be created in memory and overrite any 445 * existing XMLClassPreference for the same class in the user or system 446 * cache, whichever is appropriate. 447 * 448 * @param is an Input stream to read the XML from 449 * 450 * @return true if the file was successfully imported. 451 */ 452 public static boolean importXML(String className, boolean isUserNode) 453 throws IOException, InvalidPreferencesFormatException { 454 //System.out.println("XMLClassPrefs.importXML: in importXML class name is " 455 // + className + " and isUserNode is " + String.valueOf(isUserNode)); 456 try { 457 458 FileInputStream is = 459 new FileInputStream(getLocalXMLFilePath(className, isUserNode)); 460 try { 461 if (is.available() > -1) { 462 // yay 463 } else { 464 return false; 465 } 466 } catch (IOException f) { 467 return false; 468 } 469 Document doc = load(is); 470 String xmlVersion = 471 ((Element) doc.getChildNodes().item(1)).getAttribute( 472 "EXTERNAL_XML_VERSION"); 473 if (xmlVersion.compareTo(EXTERNAL_XML_VERSION) > 0) 474 throw new InvalidPreferencesFormatException( 475 "Exported preferences file format version " 476 + xmlVersion 477 + " is not supported. This java installation can read" 478 + " versions " 479 + EXTERNAL_XML_VERSION 480 + " or older. You may need" 481 + " to install a newer version of XMLClassPreferences. http://tools.masukomi.org/."); 482 483 Element xmlRoot = 484 (Element) doc.getChildNodes().item(1).getChildNodes().item(0); 485 486 if (xmlRoot.getAttribute("type").equals("user") && (!isUserNode)) { 487 throw new InvalidPreferencesFormatException("File claims to be of the \"user\" type when \"system\" was expected"); 488 } else if ( 489 xmlRoot.getAttribute("type").equals("system") 490 && (isUserNode)) { 491 throw new InvalidPreferencesFormatException("File claims to be of the \"system\" type when \"user\" was expected"); 492 } 493 494 String looseClassName = 495 className.substring(className.lastIndexOf(".")+1); 496 497 498 XMLClassPreferences xcp = 499 new XMLClassPreferences(className, isUserNode); 500 501 importSubtree(xcp, xmlRoot, looseClassName); 502 503 } catch (SAXException e) { 504 throw new InvalidPreferencesFormatException(e); 505 } 506 return false; 507 } 508 509 /*** 510 * Recursively traverse the specified preferences node and store 511 * the described preferences into the system or current user 512 * preferences tree, as appropriate. 513 */ 514 private static boolean importSubtree( 515 XMLClassPreferences xcp, 516 Element xmlNode, 517 String looseClassName) { 518 //System.out.println("XMLCLassPrefs.importSubTree"); 519 520 NodeList xmlKids = xmlNode.getChildNodes(); 521 int numXmlKids = xmlKids.getLength(); 522 523 // Import any preferences at this node 524 Element firstXmlKid = (Element) xmlKids.item(1); 525 try { 526 if (firstXmlKid.getAttribute("name").equals(looseClassName)) { 527 importPrefs(xcp, firstXmlKid); 528 529 return true; 530 } 531 } catch (NullPointerException e) { 532 return false; 533 } 534 535 // the file was ok but the first kid wasn't the right one. 536 // go through the children 537 for (int i = 1; i < numXmlKids; i++) { 538 Element xmlKid = (Element) xmlKids.item(i); 539 540 if (importSubtree(xcp, xmlKid, looseClassName)) { 541 return true; 542 } 543 } 544 return false; 545 } 546 547 /*** 548 * Import the preferences described by the specified XML element 549 * (a map from a preferences document) into the specified 550 * preferences node. 551 */ 552 private static void importPrefs(XMLClassPreferences xcp, Element node) { 553 554 //System.out.println("XMLClassPrefs.importPrefs"); 555 556 NodeList nodeEntries = node.getChildNodes(); 557 // now we have the map element...hopefully 558 Element map = (Element) nodeEntries.item(0); 559 NodeList entries = map.getChildNodes(); 560 561 562 for (int i = 0, numEntries = entries.getLength(); 563 i < numEntries; 564 i++) { 565 if (entries.item(i).getNodeName().equals("entry")){ 566 Element entry = (Element) entries.item(i); 567 xcp.put(entry.getAttribute("key"), entry.getAttribute("value")); 568 } 569 } 570 } 571 572 /*** 573 * Load an XML document from specified input stream, which must 574 * have the requisite DTD URI. 575 */ 576 private static Document load(InputStream in) 577 throws SAXException, IOException { 578 Resolver r = new Resolver(); 579 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 580 dbf.setIgnoringElementContentWhitespace(true); 581 dbf.setValidating(true); 582 dbf.setCoalescing(true); 583 try { 584 DocumentBuilder db = dbf.newDocumentBuilder(); 585 db.setEntityResolver(new Resolver()); 586 db.setErrorHandler(new EH()); 587 return db.parse(new InputSource(in)); 588 } catch (ParserConfigurationException x) { 589 throw new Error(x); 590 } 591 } 592 593 /*** 594 * @see java.util.prefs.Preferences#isUserNode() 595 */ 596 public boolean isUserNode() { 597 return isUserNode; 598 } 599 600 /*** 601 * @see java.lang.Object#toString() 602 */ 603 public String toString() { 604 return null; 605 } 606 607 /*** 608 * Writes the current preferences to disk. 609 */ 610 public void flush() throws BackingStoreException { 611 try { 612 File outFile = new File(getLocalXMLFilePath(className, isUserNode)); 613 if (outFile.canWrite()){ 614 FileOutputStream os = new FileOutputStream(outFile); 615 616 export(os, this, false); 617 } else { 618 try { 619 FileOutputStream fos = new FileOutputStream(getLocalXMLFilePath(className, isUserNode)); 620 export(fos, this, false); 621 fos.close(); 622 } catch (FileNotFoundException e) { 623 //try { 624 File xmlPackageDir = new File(getLocalXMLFileDir(className, isUserNode)); 625 626 if (xmlPackageDir.mkdirs()) { 627 FileOutputStream fos = new FileOutputStream(getLocalXMLFilePath(className, isUserNode)); 628 export(fos, this, false); 629 fos.close(); 630 } else { 631 throw new FileNotFoundException(e.toString()); 632 } 633 //} catch (Exception f){ 634 //throw new BackingStoreException("problem while flushing to " + outFile.getAbsolutePath() + "\n"+ f); 635 //} 636 } 637 638 } 639 } catch (FileNotFoundException e) { 640 throw new BackingStoreException(e.toString()); 641 } catch (IOException e) { 642 throw new BackingStoreException(e.toString()); 643 } 644 } 645 646 /*** 647 * @see java.util.prefs.Preferences#sync() 648 */ 649 public void sync() throws BackingStoreException { 650 } 651 652 /*** 653 * Returns the preference node from the system preference tree that is 654 * associated (by convention) with the specified class's package. The 655 * convention is as follows: the absolute path name of the node is the 656 * fully qualified package name, preceded by a slash (<tt>'/'</tt>), and 657 * with each period (<tt>'.'</tt>) replaced by a slash. For example the 658 * absolute path name of the node associated with the class 659 * <tt>com.acme.widget</tt> is <tt>/com/acme/widget</tt>. 660 * 661 * <p>This convention does not apply to the unnamed package, whose 662 * associated preference node is <tt><unnamed></tt>. This node 663 * is not intended for long term use, but for convenience in the early 664 * development of programs that do not yet belong to a package, and 665 * for "throwaway" programs. <i>Valuable data should not be stored 666 * at this node as it is shared by all programs that use it.</i> 667 * 668 * <p>A class <tt>Foo</tt> wishing to access preferences pertaining to its 669 * package can obtain a preference node as follows: <pre> 670 * static Preferences prefs = Preferences.systemNodeForPackage(Foo.class); 671 * </pre> 672 * This idiom obviates the need for using a string to describe the 673 * preferences node and decreases the likelihood of a run-time failure. 674 * (If the class name is is misspelled, it will typically result in a 675 * compile-time error.) 676 * 677 * <p>Invoking this method will result in the creation of the returned 678 * node and its ancestors if they do not already exist. If the returned 679 * node did not exist prior to this call, this node and any ancestors that 680 * were created by this call are not guaranteed to become permanent until 681 * the <tt>flush</tt> method is called on the returned node (or one of its 682 * ancestors or descendants). 683 * 684 * @param c the class for whose package a system preference node is desired. 685 * @return the system preference node associated with the package of which 686 * <tt>c</tt> is a member. 687 * @throws NullPointerException if <tt>c</tt> is <tt>null</tt>. 688 * @throws SecurityException if a security manager is present and 689 * it denies <tt>RuntimePermission("preferences")</tt>. 690 * @see RuntimePermission 691 */ 692 public static XMLClassPreferences systemNodeForClass(Class c) { 693 //System.out.println("XMLClassPrefs.systemNodeForClass was called"); 694 if (cachedSystemPrefs.containsKey(c.getName())) { 695 //System.out.println("XMLClassPrefs.systemNodeForClass returning cached"); 696 return new XMLClassPreferences(c.getName(), false); 697 } else { 698 //System.out.println("XMLClassPrefs.systemNodeForClass trying import"); 699 try { 700 importXML(c.getName(), false); 701 return new XMLClassPreferences(c.getName(), false); 702 } catch (IOException e) { 703 return new XMLClassPreferences(c.getName(), false); 704 } catch (InvalidPreferencesFormatException e) { 705 return new XMLClassPreferences(c.getName(), false); 706 } 707 } 708 } 709 /*** 710 * Returns the preference node from the calling user's preference tree 711 * that is associated (by convention) with the specified class. The 712 * convention is as follows: the absolute path name of the node is the fully 713 * qualified package name, preceded by a slash (<tt>'/'</tt>), and with each 714 * period (<tt>'.'</tt>) replaced by a slash. For example the absolute path 715 * name of the node associated with the class 716 * <tt>com.acme.widget</tt> is <tt><prefs home>/com/acme/widget. 717 * xml</tt>. 718 * 719 * <p>This convention does not apply to the unnamed class, whose associated 720 * preference node is <tt><unnamed></tt>. This node is not intended 721 * for long term use, but for convenience in the early development of 722 * programs that do not yet belong to a package, and for "throwaway" 723 * programs. <i>Valuable data should not be stored at this node as it is 724 * shared by all programs that use it.</i> 725 * 726 * <p>A class <tt>Foo</tt> wishing to access preferences pertaining to 727 * itself can obtain a preference node as follows: <pre> static 728 * Preferences prefs = Preferences.userNodeForClass(Foo.class); 729 * </pre> 730 * This idiom obviates the need for using a string to describe the 731 * preferences node and decreases the likelihood of a run-time failure. 732 * (If the class name is is misspelled, it will typically result in a 733 * compile-time error.) 734 * 735 * <p>Invoking this method will result in the creation of the returned 736 * node and its ancestors if they do not already exist. If the returned 737 * node did not exist prior to this call, this node and any ancestors that 738 * were created by this call are not guaranteed to become permanent until 739 * the <tt>flush</tt> method is called on the returned node (or one of its 740 * ancestors or descendants). 741 * 742 * @param c the class for whose package a user preference node is desired. 743 * @return the user preference node associated with the package of which 744 * <tt>c</tt> is a member. 745 * @throws NullPointerException if <tt>c</tt> is <tt>null</tt>. 746 * @throws SecurityException if a security manager is present and 747 * it denies <tt>RuntimePermission("preferences")</tt>. 748 * @see RuntimePermission 749 */ 750 public static XMLClassPreferences userNodeForClass(Class c) { 751 if (cachedUserPrefs.containsKey(System.getProperty("user.name"), c.getName())) { 752 return new XMLClassPreferences(c.getName(), true); 753 } else { 754 try { 755 importXML(c.getName(), true); 756 return new XMLClassPreferences(c.getName(), true); 757 } catch (IOException e) { 758 return new XMLClassPreferences(c.getName(), true); 759 } catch (InvalidPreferencesFormatException e) { 760 return new XMLClassPreferences(c.getName(), true); 761 } 762 } 763 } 764 765 /*** 766 * @see java.util.prefs.Preferences#addPreferenceChangeListener(java.util.prefs.PreferenceChangeListener) 767 */ 768 public void addPreferenceChangeListener(PreferenceChangeListener arg0) { 769 } 770 771 /*** 772 * @see java.util.prefs.Preferences#removePreferenceChangeListener(java.util.prefs.PreferenceChangeListener) 773 */ 774 public void removePreferenceChangeListener(PreferenceChangeListener arg0) { 775 } 776 777 /*** 778 * @see java.util.prefs.Preferences#addNodeChangeListener(java.util.prefs.NodeChangeListener) 779 */ 780 public void addNodeChangeListener(NodeChangeListener arg0) { 781 } 782 783 /*** 784 * @see java.util.prefs.Preferences#removeNodeChangeListener(java.util.prefs.NodeChangeListener) 785 */ 786 public void removeNodeChangeListener(NodeChangeListener arg0) { 787 } 788 789 790 /*** 791 * Export the specified preferences node and, if subTree is true, all 792 * subnodes, to the specified output stream. Preferences are exported as 793 * an XML document conforming to the definition in the Preferences spec. 794 * 795 * @throws IOException if writing to the specified output stream 796 * results in an <tt>IOException</tt>. 797 * @throws BackingStoreException if preference data cannot be read from 798 * backing store. 799 * @throws IllegalStateException if this node (or an ancestor) has been 800 * removed with the {@link #removeNode()} method. 801 */ 802 static void export(OutputStream os, final XMLClassPreferences xcp, boolean subTree) 803 throws IOException, BackingStoreException { 804 // subtree will always be ignored as this doens't handle children 805 if (os == null){ 806 throw new NullPointerException("null OutputStream passed to XMLClassPreferences.export"); 807 } else if (xcp == null){ 808 throw new NullPointerException("null XMLClassPreferences passed to XMLClassPreferences.export"); 809 } 810 811 XmlDocument doc = new XmlDocument(); 812 doc.setDoctype(null, PREFS_DTD_URI, null); 813 //Comment comment = doc.createComment(" File generated by org.masukomi.prefs.XMLClassPreferences. " 814 // + "\n Details at http://tools.masukomi.org"); 815 //doc.appendChild(comment); 816 Element preferences = (Element) 817 doc.appendChild(doc.createElement("preferences")); 818 preferences.setAttribute("EXTERNAL_XML_VERSION", EXTERNAL_XML_VERSION); 819 Element xmlRoot = (Element) 820 preferences.appendChild(doc.createElement("root")); 821 if (xcp.isUserNode()){ 822 xmlRoot.setAttribute("type", "user"); 823 } else { 824 xmlRoot.setAttribute("type", "system"); 825 } 826 // Get bottom-up list of nodes from p to root, excluding root 827 //List ancestors = new ArrayList(); 828 829 // in order to get this we're just going to break the className into pieces. 830 StringTokenizer tokenizer = new StringTokenizer(xcp.getClassName(), "."); 831 String[] classPath = new String[tokenizer.countTokens()]; 832 int arrayCounter =0; 833 while(tokenizer.hasMoreTokens()){ 834 classPath[arrayCounter] = tokenizer.nextToken(); 835 arrayCounter++; 836 } 837 838 Element e = xmlRoot; 839 840 for (int x = 0; x < classPath.length; x++){ 841 e.appendChild(doc.createElement("map")); 842 e = (Element) e.appendChild(doc.createElement("node")); 843 e.setAttribute("name", classPath[x]); 844 } 845 putPreferencesInXml(e, doc, xcp, subTree); 846 847 doc.write(os); 848 } 849 /*** 850 * Put the preferences in the specified Preferences node into the 851 * specified XML element which is assumed to represent a node 852 * in the specified XML document which is assumed to conform to 853 * PREFS_DTD. If subTree is true, create children of the specified 854 * XML node conforming to all of the children of the specified 855 * Preferences node and recurse. 856 * 857 * @throws BackingStoreException if it is not possible to read 858 * the preferences or children out of the specified 859 * preferences node. 860 */ 861 private static void putPreferencesInXml(Element elt, Document doc, 862 XMLClassPreferences xcp, boolean subTree) throws BackingStoreException { 863 //Preferences[] kidsCopy = null; 864 String[] kidNames = null; 865 866 867 868 // Put map in xml element 869 String[] keys = xcp.keys(); 870 Element map = (Element) elt.appendChild(doc.createElement("map")); 871 for (int i=0; i<keys.length; i++) { 872 Element entry = (Element) 873 map.appendChild(doc.createElement("entry")); 874 entry.setAttribute("key", keys[i]); 875 // NEXT STATEMENT THROWS NULL PTR EXC INSTEAD OF ASSERT FAIL 876 //try{ 877 878 entry.setAttribute("value", xcp.get(keys[i], null)); 879 //} catch (NullPointerException e){ 880 // this line is a test only.... 881 // entry.setAttribute("value", xcp.get(keys[i], "")); 882 //} 883 } 884 885 } 886 887 888 /*** 889 * @see java.util.prefs.Preferences#exportNode(java.io.OutputStream) 890 */ 891 public void exportNode(OutputStream arg0) 892 throws IOException, BackingStoreException { 893 } 894 895 /*** 896 * @see java.util.prefs.Preferences#exportSubtree(java.io.OutputStream) 897 */ 898 public void exportSubtree(OutputStream arg0) 899 throws IOException, BackingStoreException { 900 } 901 902 ///////////////////////////////////////////// 903 904 private static class Resolver implements EntityResolver { 905 public InputSource resolveEntity(String pid, String sid) 906 throws SAXException { 907 if (sid.equals(PREFS_DTD_URI)) { 908 InputSource is; 909 is = new InputSource(new StringReader(PREFS_DTD)); 910 is.setSystemId(PREFS_DTD_URI); 911 return is; 912 } 913 throw new SAXException("Invalid system identifier: " + sid); 914 } 915 } 916 917 private static class EH implements ErrorHandler { 918 public void error(SAXParseException x) throws SAXException { 919 throw x; 920 } 921 public void fatalError(SAXParseException x) throws SAXException { 922 throw x; 923 } 924 public void warning(SAXParseException x) throws SAXException { 925 throw x; 926 } 927 } 928 929 } 930

This page was automatically generated by Maven