1 7 8 package java.util.prefs; 9 10 import java.util.*; 11 import java.io.*; 12 import java.security.AccessController ; 13 import java.security.PrivilegedAction ; 14 import java.lang.Integer ; 16 import java.lang.Long ; 17 import java.lang.Float ; 18 import java.lang.Double ; 19 20 106 public abstract class AbstractPreferences extends Preferences { 107 110 private final String name; 111 112 115 private final String absolutePath; 116 117 120 final AbstractPreferences parent; 121 122 125 private final AbstractPreferences root; 127 135 protected boolean newNode = false; 136 137 141 private Map kidCache = new HashMap(); 142 143 147 private boolean removed = false; 148 149 152 private PreferenceChangeListener [] prefListeners = 153 new PreferenceChangeListener [0]; 154 155 158 private NodeChangeListener [] nodeListeners = new NodeChangeListener [0]; 159 160 167 protected final Object lock = new Object (); 168 169 181 protected AbstractPreferences(AbstractPreferences parent, String name) { 182 if (parent==null) { 183 if (!name.equals("")) 184 throw new IllegalArgumentException ("Root name '"+name+ 185 "' must be \"\""); 186 this.absolutePath = "/"; 187 root = this; 188 } else { 189 if (name.indexOf('/') != -1) 190 throw new IllegalArgumentException ("Name '" + name + 191 "' contains '/'"); 192 if (name.equals("")) 193 throw new IllegalArgumentException ("Illegal name: empty string"); 194 195 root = parent.root; 196 absolutePath = (parent==root ? "/" + name 197 : parent.absolutePath() + "/" + name); 198 } 199 this.name = name; 200 this.parent = parent; 201 } 202 203 222 public void put(String key, String value) { 223 if (key==null || value==null) 224 throw new NullPointerException (); 225 if (key.length() > MAX_KEY_LENGTH) 226 throw new IllegalArgumentException ("Key too long: "+key); 227 if (value.length() > MAX_VALUE_LENGTH) 228 throw new IllegalArgumentException ("Value too long: "+value); 229 230 synchronized(lock) { 231 if (removed) 232 throw new IllegalStateException ("Node has been removed."); 233 234 putSpi(key, value); 235 enqueuePreferenceChangeEvent(key, value); 236 } 237 } 238 239 261 public String get(String key, String def) { 262 if (key==null) 263 throw new NullPointerException ("Null key"); 264 synchronized(lock) { 265 if (removed) 266 throw new IllegalStateException ("Node has been removed."); 267 268 String result = null; 269 try { 270 result = getSpi(key); 271 } catch (Exception e) { 272 } 274 return (result==null ? def : result); 275 } 276 } 277 278 292 public void remove(String key) { 293 synchronized(lock) { 294 if (removed) 295 throw new IllegalStateException ("Node has been removed."); 296 297 removeSpi(key); 298 enqueuePreferenceChangeEvent(key, null); 299 } 300 } 301 302 316 public void clear() throws BackingStoreException { 317 synchronized(lock) { 318 String [] keys = keys(); 319 for (int i=0; i<keys.length; i++) 320 remove(keys[i]); 321 } 322 } 323 324 340 public void putInt(String key, int value) { 341 put(key, Integer.toString(value)); 342 } 343 344 366 public int getInt(String key, int def) { 367 int result = def; 368 try { 369 String value = get(key, null); 370 if (value != null) 371 result = Integer.parseInt(value); 372 } catch (NumberFormatException e) { 373 } 375 376 return result; 377 } 378 379 395 public void putLong(String key, long value) { 396 put(key, Long.toString(value)); 397 } 398 399 421 public long getLong(String key, long def) { 422 long result = def; 423 try { 424 String value = get(key, null); 425 if (value != null) 426 result = Long.parseLong(value); 427 } catch (NumberFormatException e) { 428 } 430 431 return result; 432 } 433 434 450 public void putBoolean(String key, boolean value) { 451 put(key, String.valueOf(value)); 452 } 453 454 479 public boolean getBoolean(String key, boolean def) { 480 boolean result = def; 481 String value = get(key, null); 482 if (value != null) { 483 if (value.equalsIgnoreCase("true")) 484 result = true; 485 else if (value.equalsIgnoreCase("false")) 486 result = false; 487 } 488 489 return result; 490 } 491 492 508 public void putFloat(String key, float value) { 509 put(key, Float.toString(value)); 510 } 511 512 534 public float getFloat(String key, float def) { 535 float result = def; 536 try { 537 String value = get(key, null); 538 if (value != null) 539 result = Float.parseFloat(value); 540 } catch (NumberFormatException e) { 541 } 543 544 return result; 545 } 546 547 563 public void putDouble(String key, double value) { 564 put(key, Double.toString(value)); 565 } 566 567 589 public double getDouble(String key, double def) { 590 double result = def; 591 try { 592 String value = get(key, null); 593 if (value != null) 594 result = Double.parseDouble(value); 595 } catch (NumberFormatException e) { 596 } 598 599 return result; 600 } 601 602 614 public void putByteArray(String key, byte[] value) { 615 put(key, Base64.byteArrayToBase64(value)); 616 } 617 618 635 public byte[] getByteArray(String key, byte[] def) { 636 byte[] result = def; 637 String value = get(key, null); 638 try { 639 if (value != null) 640 result = Base64.base64ToByteArray(value); 641 } 642 catch (RuntimeException e) { 643 } 645 646 return result; 647 } 648 649 664 public String [] keys() throws BackingStoreException { 665 synchronized(lock) { 666 if (removed) 667 throw new IllegalStateException ("Node has been removed."); 668 669 return keysSpi(); 670 } 671 } 672 673 693 public String [] childrenNames() throws BackingStoreException { 694 synchronized(lock) { 695 if (removed) 696 throw new IllegalStateException ("Node has been removed."); 697 698 Set s = new TreeSet(kidCache.keySet()); 699 String [] kids = childrenNamesSpi(); 700 for(int i=0; i<kids.length; i++) 701 s.add(kids[i]); 702 return (String []) s.toArray(EMPTY_STRING_ARRAY); 703 } 704 } 705 706 private static final String [] EMPTY_STRING_ARRAY = new String [0]; 707 708 713 protected final AbstractPreferences [] cachedChildren() { 714 return (AbstractPreferences []) kidCache.values(). 715 toArray(EMPTY_ABSTRACT_PREFS_ARRAY); 716 } 717 718 private static final AbstractPreferences [] EMPTY_ABSTRACT_PREFS_ARRAY 719 = new AbstractPreferences [0]; 720 721 733 public Preferences parent() { 734 synchronized(lock) { 735 if (removed) 736 throw new IllegalStateException ("Node has been removed."); 737 738 return parent; 739 } 740 } 741 742 787 public Preferences node(String path) { 788 synchronized(lock) { 789 if (removed) 790 throw new IllegalStateException ("Node has been removed."); 791 if (path.equals("")) 792 return this; 793 if (path.equals("/")) 794 return root; 795 if (path.charAt(0) != '/') 796 return node(new StringTokenizer(path, "/", true)); 797 } 798 799 return root.node(new StringTokenizer(path.substring(1), "/", true)); 801 } 802 803 806 private Preferences node(StringTokenizer path) { 807 String token = path.nextToken(); 808 if (token.equals("/")) throw new IllegalArgumentException ("Consecutive slashes in path"); 810 synchronized(lock) { 811 AbstractPreferences child=(AbstractPreferences )kidCache.get(token); 812 if (child == null) { 813 if (token.length() > MAX_NAME_LENGTH) 814 throw new IllegalArgumentException ( 815 "Node name " + token + " too long"); 816 child = childSpi(token); 817 if (child.newNode) 818 enqueueNodeAddedEvent(child); 819 kidCache.put(token, child); 820 } 821 if (!path.hasMoreTokens()) 822 return child; 823 path.nextToken(); if (!path.hasMoreTokens()) 825 throw new IllegalArgumentException ("Path ends with slash"); 826 return child.node(path); 827 } 828 } 829 830 850 public boolean nodeExists(String path) 851 throws BackingStoreException 852 { 853 synchronized(lock) { 854 if (path.equals("")) 855 return !removed; 856 if (removed) 857 throw new IllegalStateException ("Node has been removed."); 858 if (path.equals("/")) 859 return true; 860 if (path.charAt(0) != '/') 861 return nodeExists(new StringTokenizer(path, "/", true)); 862 } 863 864 return root.nodeExists(new StringTokenizer(path.substring(1), "/", 866 true)); 867 } 868 869 872 private boolean nodeExists(StringTokenizer path) 873 throws BackingStoreException 874 { 875 String token = path.nextToken(); 876 if (token.equals("/")) throw new IllegalArgumentException ("Consecutive slashes in path"); 878 synchronized(lock) { 879 AbstractPreferences child=(AbstractPreferences )kidCache.get(token); 880 if (child == null) 881 child = getChild(token); 882 if (child==null) 883 return false; 884 if (!path.hasMoreTokens()) 885 return true; 886 path.nextToken(); if (!path.hasMoreTokens()) 888 throw new IllegalArgumentException ("Path ends with slash"); 889 return child.nodeExists(path); 890 } 891 } 892 893 925 public void removeNode() throws BackingStoreException { 926 if (this==root) 927 throw new UnsupportedOperationException ("Can't remove the root!"); 928 synchronized(parent.lock) { 929 removeNode2(); 930 parent.kidCache.remove(name); 931 } 932 } 933 934 938 private void removeNode2() throws BackingStoreException { 939 synchronized(lock) { 940 if (removed) 941 throw new IllegalStateException ("Node already removed."); 942 943 String [] kidNames = childrenNamesSpi(); 945 for (int i=0; i<kidNames.length; i++) 946 if (!kidCache.containsKey(kidNames[i])) 947 kidCache.put(kidNames[i], childSpi(kidNames[i])); 948 949 for (Iterator i=kidCache.values().iterator(); i.hasNext(); ) 951 ((AbstractPreferences )i.next()).removeNode2(); 952 kidCache.clear(); 953 954 removeNodeSpi(); 956 removed = true; 957 parent.enqueueNodeRemovedEvent(this); 958 } 959 } 960 961 970 public String name() { 971 return name; 972 } 973 974 985 public String absolutePath() { 986 return absolutePath; 987 } 988 989 1002 public boolean isUserNode() { 1003 Boolean result = (Boolean ) 1004 AccessController.doPrivileged( new PrivilegedAction () { 1005 public Object run() { 1006 return new Boolean (root == Preferences.userRoot()); 1007 } 1008 }); 1009 return result.booleanValue(); 1010 } 1011 1012 public void addPreferenceChangeListener(PreferenceChangeListener pcl) { 1013 if (pcl==null) 1014 throw new NullPointerException ("Change listener is null."); 1015 synchronized(lock) { 1016 if (removed) 1017 throw new IllegalStateException ("Node has been removed."); 1018 1019 PreferenceChangeListener [] old = prefListeners; 1021 prefListeners = new PreferenceChangeListener [old.length + 1]; 1022 System.arraycopy(old, 0, prefListeners, 0, old.length); 1023 prefListeners[old.length] = pcl; 1024 } 1025 startEventDispatchThreadIfNecessary(); 1026 } 1027 1028 public void removePreferenceChangeListener(PreferenceChangeListener pcl) { 1029 synchronized(lock) { 1030 if (removed) 1031 throw new IllegalStateException ("Node has been removed."); 1032 if ((prefListeners == null) || (prefListeners.length == 0)) 1033 throw new IllegalArgumentException ("Listener not registered."); 1034 1035 PreferenceChangeListener [] newPl = 1037 new PreferenceChangeListener [prefListeners.length - 1]; 1038 int i = 0; 1039 while (i < newPl.length && prefListeners[i] != pcl) 1040 newPl[i] = prefListeners[i++]; 1041 1042 if (i == newPl.length && prefListeners[i] != pcl) 1043 throw new IllegalArgumentException ("Listener not registered."); 1044 while (i < newPl.length) 1045 newPl[i] = prefListeners[++i]; 1046 prefListeners = newPl; 1047 } 1048 } 1049 1050 public void addNodeChangeListener(NodeChangeListener ncl) { 1051 if (ncl==null) 1052 throw new NullPointerException ("Change listener is null."); 1053 synchronized(lock) { 1054 if (removed) 1055 throw new IllegalStateException ("Node has been removed."); 1056 1057 if (nodeListeners == null) { 1059 nodeListeners = new NodeChangeListener [1]; 1060 nodeListeners[0] = ncl; 1061 } else { 1062 NodeChangeListener [] old = nodeListeners; 1063 nodeListeners = new NodeChangeListener [old.length + 1]; 1064 System.arraycopy(old, 0, nodeListeners, 0, old.length); 1065 nodeListeners[old.length] = ncl; 1066 } 1067 } 1068 startEventDispatchThreadIfNecessary(); 1069 } 1070 1071 public void removeNodeChangeListener(NodeChangeListener ncl) { 1072 synchronized(lock) { 1073 if (removed) 1074 throw new IllegalStateException ("Node has been removed."); 1075 if ((nodeListeners == null) || (nodeListeners.length == 0)) 1076 throw new IllegalArgumentException ("Listener not registered."); 1077 1078 NodeChangeListener [] newNl = 1080 new NodeChangeListener [nodeListeners.length - 1]; 1081 int i = 0; 1082 while (i < nodeListeners.length && nodeListeners[i] != ncl) 1083 newNl[i] = nodeListeners[i++]; 1084 1085 if (i == nodeListeners.length) 1086 throw new IllegalArgumentException ("Listener not registered."); 1087 while (i < newNl.length) 1088 newNl[i] = nodeListeners[++i]; 1089 nodeListeners = newNl; 1090 } 1091 } 1092 1093 1095 1103 protected abstract void putSpi(String key, String value); 1104 1105 1124 protected abstract String getSpi(String key); 1125 1126 1134 protected abstract void removeSpi(String key); 1135 1136 1158 protected abstract void removeNodeSpi() throws BackingStoreException ; 1159 1160 1177 protected abstract String [] keysSpi() throws BackingStoreException ; 1178 1179 1197 protected abstract String [] childrenNamesSpi() 1198 throws BackingStoreException ; 1199 1200 1229 protected AbstractPreferences getChild(String nodeName) 1230 throws BackingStoreException { 1231 synchronized(lock) { 1232 String [] kidNames = childrenNames(); 1234 for (int i=0; i<kidNames.length; i++) 1235 if (kidNames[i].equals(nodeName)) 1236 return childSpi(kidNames[i]); 1237 } 1238 return null; 1239 } 1240 1241 1272 protected abstract AbstractPreferences childSpi(String name); 1273 1274 1277 public String toString() { 1278 return (this.isUserNode() ? "User" : "System") + 1279 " Preference Node: " + this.absolutePath(); 1280 } 1281 1282 1302 public void sync() throws BackingStoreException { 1303 sync2(); 1304 } 1305 1306 private void sync2() throws BackingStoreException { 1307 AbstractPreferences [] cachedKids; 1308 1309 synchronized(lock) { 1310 if (removed) 1311 throw new IllegalStateException ("Node has been removed"); 1312 syncSpi(); 1313 cachedKids = cachedChildren(); 1314 } 1315 1316 for (int i=0; i<cachedKids.length; i++) 1317 cachedKids[i].sync2(); 1318 } 1319 1320 1338 protected abstract void syncSpi() throws BackingStoreException ; 1339 1340 1362 public void flush() throws BackingStoreException { 1363 flush2(); 1364 } 1365 1366 private void flush2() throws BackingStoreException { 1367 AbstractPreferences [] cachedKids; 1368 1369 synchronized(lock) { 1370 flushSpi(); 1371 if(removed) 1372 return; 1373 cachedKids = cachedChildren(); 1374 } 1375 1376 for (int i = 0; i < cachedKids.length; i++) 1377 cachedKids[i].flush2(); 1378 } 1379 1380 1399 protected abstract void flushSpi() throws BackingStoreException ; 1400 1401 1410 protected boolean isRemoved() { 1411 synchronized(lock) { 1412 return removed; 1413 } 1414 } 1415 1416 1424 private static final List eventQueue = new LinkedList(); 1425 1426 1431 private class NodeAddedEvent extends NodeChangeEvent { 1432 private static final long serialVersionUID = -6743557530157328528L; 1433 NodeAddedEvent(Preferences parent, Preferences child) { 1434 super(parent, child); 1435 } 1436 } 1437 private class NodeRemovedEvent extends NodeChangeEvent { 1438 private static final long serialVersionUID = 8735497392918824837L; 1439 NodeRemovedEvent(Preferences parent, Preferences child) { 1440 super(parent, child); 1441 } 1442 } 1443 1444 1448 private static class EventDispatchThread extends Thread { 1449 public void run() { 1450 while(true) { 1451 EventObject event = null; 1453 synchronized(eventQueue) { 1454 try { 1455 while (eventQueue.isEmpty()) 1456 eventQueue.wait(); 1457 event = (EventObject) eventQueue.remove(0); 1458 } catch (InterruptedException e) { 1459 return; 1461 } 1462 } 1463 1464 AbstractPreferences SRC=(AbstractPreferences )event.getSource(); 1466 if (event instanceof PreferenceChangeEvent ) { 1467 PreferenceChangeEvent pce = (PreferenceChangeEvent )event; 1468 PreferenceChangeListener [] listeners = src.prefListeners(); 1469 for (int i=0; i<listeners.length; i++) 1470 listeners[i].preferenceChange(pce); 1471 } else { 1472 NodeChangeEvent nce = (NodeChangeEvent )event; 1473 NodeChangeListener [] listeners = src.nodeListeners(); 1474 if (nce instanceof NodeAddedEvent) { 1475 for (int i=0; i<listeners.length; i++) 1476 listeners[i].childAdded(nce); 1477 } else { 1478 for (int i=0; i<listeners.length; i++) 1480 listeners[i].childRemoved(nce); 1481 } 1482 } 1483 } 1484 } 1485 } 1486 1487 private static Thread eventDispatchThread = null; 1488 1489 1494 private static synchronized void startEventDispatchThreadIfNecessary() { 1495 if (eventDispatchThread == null) { 1496 eventDispatchThread = new EventDispatchThread(); 1498 eventDispatchThread.setDaemon(true); 1499 eventDispatchThread.start(); 1500 } 1501 } 1502 1503 1509 PreferenceChangeListener [] prefListeners() { 1510 synchronized(lock) { 1511 return prefListeners; 1512 } 1513 } 1514 NodeChangeListener [] nodeListeners() { 1515 synchronized(lock) { 1516 return nodeListeners; 1517 } 1518 } 1519 1520 1525 private void enqueuePreferenceChangeEvent(String key, String newValue) { 1526 if (prefListeners.length != 0) { 1527 synchronized(eventQueue) { 1528 eventQueue.add(new PreferenceChangeEvent (this, key, newValue)); 1529 eventQueue.notify(); 1530 } 1531 } 1532 } 1533 1534 1539 private void enqueueNodeAddedEvent(Preferences child) { 1540 if (nodeListeners.length != 0) { 1541 synchronized(eventQueue) { 1542 eventQueue.add(new NodeAddedEvent(this, child)); 1543 eventQueue.notify(); 1544 } 1545 } 1546 } 1547 1548 1553 private void enqueueNodeRemovedEvent(Preferences child) { 1554 if (nodeListeners.length != 0) { 1555 synchronized(eventQueue) { 1556 eventQueue.add(new NodeRemovedEvent(this, child)); 1557 eventQueue.notify(); 1558 } 1559 } 1560 } 1561 1562 1572 public void exportNode(OutputStream os) 1573 throws IOException, BackingStoreException 1574 { 1575 XmlSupport.export(os, this, false); 1576 } 1577 1578 1588 public void exportSubtree(OutputStream os) 1589 throws IOException, BackingStoreException 1590 { 1591 XmlSupport.export(os, this, true); 1592 } 1593} 1594 | Popular Tags |