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