001: /*
002:
003: ============================================================================
004: The Apache Software License, Version 1.1
005: ============================================================================
006:
007: Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
008:
009: Redistribution and use in source and binary forms, with or without modifica-
010: tion, are permitted provided that the following conditions are met:
011:
012: 1. Redistributions of source code must retain the above copyright notice,
013: this list of conditions and the following disclaimer.
014:
015: 2. Redistributions in binary form must reproduce the above copyright notice,
016: this list of conditions and the following disclaimer in the documentation
017: and/or other materials provided with the distribution.
018:
019: 3. The end-user documentation included with the redistribution, if any, must
020: include the following acknowledgment: "This product includes software
021: developed by the Apache Software Foundation (http://www.apache.org/)."
022: Alternately, this acknowledgment may appear in the software itself, if
023: and wherever such third-party acknowledgments normally appear.
024:
025: 4. The names "Batik" and "Apache Software Foundation" must not be
026: used to endorse or promote products derived from this software without
027: prior written permission. For written permission, please contact
028: apache@apache.org.
029:
030: 5. Products derived from this software may not be called "Apache", nor may
031: "Apache" appear in their name, without prior written permission of the
032: Apache Software Foundation.
033:
034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
035: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
036: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
037: APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
038: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
039: DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
040: OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
041: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
042: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
043: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: This software consists of voluntary contributions made by many individuals
046: on behalf of the Apache Software Foundation. For more information on the
047: Apache Software Foundation, please see <http://www.apache.org/>.
048:
049: */
050:
051: package org.apache.batik.util;
052:
053: import java.awt.Color;
054: import java.awt.Dimension;
055: import java.awt.Font;
056: import java.awt.Point;
057: import java.awt.Rectangle;
058: import java.io.File;
059: import java.io.FileInputStream;
060: import java.io.FileOutputStream;
061: import java.io.IOException;
062: import java.net.MalformedURLException;
063: import java.net.URL;
064: import java.security.AccessControlException;
065: import java.util.ArrayList;
066: import java.util.Map;
067: import java.util.Properties;
068: import java.util.StringTokenizer;
069:
070: /**
071: * This class allows to manage users preferences.
072: * <p>
073: * Here is a short usage example:
074: * <p>
075: * <blockquote><pre>
076: * // at application intialization
077: * HashMap defaults = new HashMap();
078: * defaults.put("windowSize", new Dimension(640, 480));
079: * defaults.put("antialias", Boolean.TRUE);
080: * PreferenceManager prefs = new PreferenceManager("application.ini", defaults);
081: * try {
082: * prefs.load();
083: * } catch (IOException e) {
084: * //
085: * }
086: * myApplication.setSize(prefs.getDimension("windowSize"));
087: * myApplication.setAntialiasingOn(prefs.getBoolean("antialias"));
088: *
089: * // later a dialog box may customize preferences
090: * myApplication.setAntialiasingOn(antialiasCheckBox.getState());
091: * prefs.setBoolean("antialias", antialiasCheckBox.getState());
092: *
093: * // when leaving the application we need to save the preferences
094: * prefs.setDimension("windowSize", myApplication.getSize());
095: * prefs.setFiles("history", lastVisitedFileArray);
096: * try {
097: * prefs.save()
098: * } catch (IOException e) {
099: * //
100: * }
101: * </pre></blockquote>
102: * <p>
103: * @author <a href="mailto:cjolif@ilog.fr">Christophe Jolif</a>
104: * @version $Id$
105: */
106: public class PreferenceManager {
107: protected Properties internal = null;
108: protected Map defaults = null;
109: protected String prefFileName = null;
110: protected String fullName = null;
111:
112: protected final static String USER_HOME = getSystemProperty("user.home");
113: protected final static String USER_DIR = getSystemProperty("user.dir");
114: protected final static String FILE_SEP = getSystemProperty("file.separator");
115:
116: private static String PREF_DIR = null;
117:
118: /**
119: * Gets a System property if accessible. Returns an empty string
120: * otherwise
121: */
122: protected static String getSystemProperty(String prop) {
123: try {
124: return System.getProperty(prop);
125: } catch (AccessControlException e) {
126: return "";
127: }
128: }
129:
130: /**
131: * Creates a preference manager.
132: * @param prefFileName the name of the preference file.
133: */
134: public PreferenceManager(String prefFileName) {
135: this (prefFileName, null);
136: }
137:
138: /**
139: * Creates a preference manager with a default values
140: * initialization map.
141: * @param prefFileName the name of the preference file.
142: * @param defaults where to get defaults value if the value is
143: * not specified in the file.
144: */
145: public PreferenceManager(String prefFileName, Map defaults) {
146: this .prefFileName = prefFileName;
147: this .defaults = defaults;
148: internal = new Properties();
149: }
150:
151: /**
152: * Sets a <code>String</code> representing the directory
153: * where <code>PreferenceManager</code> instances should look
154: * for preferences files. The default value is <code>null</code>
155: * which means the automatic mechanism for looking for preferences
156: * is used.
157: * @see #load
158: */
159: public static void setPreferenceDirectory(String dir) {
160: PREF_DIR = dir;
161: }
162:
163: /**
164: * Returns a <code>String</code> representing the directory
165: * where <code>PreferenceManager</code> instances should look
166: * for preferences.
167: * @see #load
168: * @see #setPreferenceDirectory
169: */
170: public static String getPreferenceDirectory() {
171: return PREF_DIR;
172: }
173:
174: /**
175: * Loads the preference file. If the file has already been previously
176: * sucessfuly loaded or saved, it will first try to reaload it from
177: * this location. Otherwise, it will try to find the file
178: * in the following order: in the directory set by
179: * {@link #setPreferenceDirectory} if it exists, in the user
180: * home directory and then in the current user directory.
181: * @exception IOException if an error occured when reading the file.
182: * @see #save
183: */
184: public void load() throws IOException {
185: FileInputStream fis = null;
186: if (fullName != null)
187: try {
188: fis = new FileInputStream(fullName);
189: } catch (IOException e1) {
190: fullName = null;
191: }
192: if (fullName == null) {
193: if (PREF_DIR != null) {
194: try {
195: fis = new FileInputStream(fullName = PREF_DIR
196: + FILE_SEP + prefFileName);
197: } catch (IOException e2) {
198: fullName = null;
199: }
200: }
201: if (fullName == null) {
202: try {
203: fis = new FileInputStream(fullName = USER_HOME
204: + FILE_SEP + prefFileName);
205: } catch (IOException e3) {
206: try {
207: fis = new FileInputStream(fullName = USER_DIR
208: + FILE_SEP + prefFileName);
209: } catch (IOException e4) {
210: fullName = null;
211: }
212: }
213: }
214: }
215: if (fullName != null) {
216: try {
217: internal.load(fis);
218: } finally {
219: fis.close();
220: }
221: }
222: }
223:
224: /**
225: * Saves the preference file. If it has previously sucessfuly been
226: * loaded or save it will save it at the same location. In other cases
227: * it will save it in the directory set by {@link #setPreferenceDirectory}
228: * if has been set and exists, otherwise in the user home directory.
229: * @exception IOException if an error occured when writing the file or
230: * if is impossible to write the file at all available locations.
231: * @see #load
232: */
233: public void save() throws IOException {
234: FileOutputStream fos = null;
235: if (fullName != null)
236: try {
237: fos = new FileOutputStream(fullName);
238: } catch (IOException e1) {
239: fullName = null;
240: }
241: if (fullName == null) {
242: if (PREF_DIR != null) {
243: try {
244: fos = new FileOutputStream(fullName = PREF_DIR
245: + FILE_SEP + prefFileName);
246: } catch (IOException e2) {
247: fullName = null;
248: }
249: }
250: if (fullName == null) {
251: try {
252: fos = new FileOutputStream(fullName = USER_HOME
253: + FILE_SEP + prefFileName);
254: } catch (IOException e3) {
255: fullName = null;
256: throw e3;
257: }
258: }
259: }
260: try {
261: internal.store(fos, prefFileName);
262: } finally {
263: fos.close();
264: }
265: }
266:
267: private Object getDefault(String key) {
268: if (defaults != null)
269: return defaults.get(key);
270: else
271: return null;
272: }
273:
274: /**
275: * Returns a Rectangle preference.
276: */
277: public Rectangle getRectangle(String key) {
278: Rectangle defaultValue = (Rectangle) getDefault(key);
279: String sp = internal.getProperty(key);
280: if (sp == null) {
281: return defaultValue;
282: }
283: Rectangle result = new Rectangle();
284: try {
285: int x, y, w, h;
286: String token;
287: StringTokenizer st = new StringTokenizer(sp, " ", false);
288: if (!st.hasMoreTokens()) {
289: // the value is not correctly formated => remove it
290: internal.remove(key);
291: return defaultValue;
292: }
293: token = st.nextToken();
294: x = Integer.parseInt(token);
295: if (!st.hasMoreTokens()) {
296: internal.remove(key);
297: return defaultValue;
298: }
299: token = st.nextToken();
300: y = Integer.parseInt(token);
301: if (!st.hasMoreTokens()) {
302: internal.remove(key);
303: return defaultValue;
304: }
305: token = st.nextToken();
306: w = Integer.parseInt(token);
307: if (!st.hasMoreTokens()) {
308: internal.remove(key);
309: return defaultValue;
310: }
311: token = st.nextToken();
312: h = Integer.parseInt(token);
313: result.setBounds(x, y, w, h);
314: return result;
315: } catch (NumberFormatException e) {
316: internal.remove(key);
317: return defaultValue;
318: }
319: }
320:
321: /**
322: * Returns a Dimension preference.
323: */
324: public Dimension getDimension(String key) {
325: Dimension defaultValue = (Dimension) getDefault(key);
326: String sp = internal.getProperty(key);
327: if (sp == null)
328: return defaultValue;
329: Dimension result = new Dimension();
330: try {
331: int w, h;
332: String token;
333: StringTokenizer st = new StringTokenizer(sp, " ", false);
334: if (!st.hasMoreTokens()) {
335: // the value is not correctly formated => remove it
336: internal.remove(key);
337: return defaultValue;
338: }
339: token = st.nextToken();
340: w = Integer.parseInt(token);
341: if (!st.hasMoreTokens()) {
342: internal.remove(key);
343: return defaultValue;
344: }
345: token = st.nextToken();
346: h = Integer.parseInt(token);
347: result.setSize(w, h);
348: return result;
349: } catch (NumberFormatException e) {
350: internal.remove(key);
351: return defaultValue;
352: }
353: }
354:
355: /**
356: * Returns a point preference.
357: */
358: public Point getPoint(String key) {
359: Point defaultValue = (Point) getDefault(key);
360: String sp = internal.getProperty(key);
361: if (sp == null) {
362: return defaultValue;
363: }
364: Point result = new Point();
365: try {
366: int x, y;
367: String token;
368: StringTokenizer st = new StringTokenizer(sp, " ", false);
369: if (!st.hasMoreTokens()) {
370: // the value is not correctly formated => remove it
371: internal.remove(key);
372: return defaultValue;
373: }
374: token = st.nextToken();
375: x = Integer.parseInt(token);
376: if (!st.hasMoreTokens()) {
377: internal.remove(key);
378: return defaultValue;
379: }
380: token = st.nextToken();
381: y = Integer.parseInt(token);
382: if (!st.hasMoreTokens()) {
383: internal.remove(key);
384: return defaultValue;
385: }
386: result.setLocation(x, y);
387: return result;
388: } catch (NumberFormatException e) {
389: internal.remove(key);
390: return defaultValue;
391: }
392: }
393:
394: /**
395: * Retruns a Color preference.
396: */
397: public Color getColor(String key) {
398: Color defaultValue = (Color) getDefault(key);
399: String sp = internal.getProperty(key);
400: if (sp == null) {
401: return defaultValue;
402: }
403: try {
404: int r, g, b, a;
405: String token;
406: StringTokenizer st = new StringTokenizer(sp, " ", false);
407: if (!st.hasMoreTokens()) {
408: // the value is not correctly formated => remove it
409: internal.remove(key);
410: return defaultValue;
411: }
412: token = st.nextToken();
413: r = Integer.parseInt(token);
414: if (!st.hasMoreTokens()) {
415: internal.remove(key);
416: return defaultValue;
417: }
418: token = st.nextToken();
419: g = Integer.parseInt(token);
420: if (!st.hasMoreTokens()) {
421: internal.remove(key);
422: return defaultValue;
423: }
424: token = st.nextToken();
425: b = Integer.parseInt(token);
426: if (!st.hasMoreTokens()) {
427: internal.remove(key);
428: return defaultValue;
429: }
430: token = st.nextToken();
431: a = Integer.parseInt(token);
432: return new Color(r, g, b, a);
433: } catch (NumberFormatException e) {
434: internal.remove(key);
435: return defaultValue;
436: }
437: }
438:
439: /**
440: * Returns a font preference.
441: */
442: public Font getFont(String key) {
443: Font defaultValue = (Font) getDefault(key);
444: String sp = internal.getProperty(key);
445: if (sp == null) {
446: return defaultValue;
447: }
448: try {
449: int size, type;
450: String name;
451: String token;
452: StringTokenizer st = new StringTokenizer(sp, " ", false);
453: if (!st.hasMoreTokens()) {
454: // the value is not correctly formated => remove it
455: internal.remove(key);
456: return defaultValue;
457: }
458: name = st.nextToken();
459: if (!st.hasMoreTokens()) {
460: internal.remove(key);
461: return defaultValue;
462: }
463: token = st.nextToken();
464: size = Integer.parseInt(token);
465: if (!st.hasMoreTokens()) {
466: internal.remove(key);
467: return defaultValue;
468: }
469: token = st.nextToken();
470: type = Integer.parseInt(token);
471: return new Font(name, type, size);
472: } catch (NumberFormatException e) {
473: internal.remove(key);
474: return defaultValue;
475: }
476:
477: }
478:
479: /**
480: * Returns a String preference.
481: */
482: public String getString(String key) {
483: String sp = internal.getProperty(key);
484: if (sp == null) {
485: sp = (String) getDefault(key);
486: }
487: return sp;
488: }
489:
490: /**
491: * Returns an array of String preference.
492: */
493: public String[] getStrings(String mkey) {
494: String last;
495: int i = 0;
496: ArrayList v = new ArrayList();
497: while (true) {
498: last = getString(mkey + i);
499: i++;
500: if (last == null)
501: break;
502: v.add(last);
503: }
504: if (v.size() != 0) {
505: String[] str = new String[v.size()];
506: return (String[]) v.toArray(str);
507: } else {
508: return (String[]) getDefault(mkey);
509: }
510: }
511:
512: /**
513: * Returns an URL preference.
514: */
515: public URL getURL(String key) {
516: URL defaultValue = (URL) getDefault(key);
517: String sp = internal.getProperty(key);
518: if (sp == null) {
519: return defaultValue;
520: }
521: URL url = null;
522: try {
523: url = new URL(sp);
524: } catch (MalformedURLException ex) {
525: internal.remove(key);
526: return defaultValue;
527: }
528: return url;
529: }
530:
531: /**
532: * Returns an array of URLs preference.
533: */
534: public URL[] getURLs(String mkey) {
535: URL last;
536: int i = 0;
537: ArrayList v = new ArrayList();
538: while (true) {
539: last = getURL(mkey + i);
540: i++;
541: if (last == null)
542: break;
543: v.add(last);
544: }
545: if (v.size() != 0) {
546: URL[] path = new URL[v.size()];
547: return (URL[]) v.toArray(path);
548: } else {
549: return (URL[]) getDefault(mkey);
550: }
551: }
552:
553: /**
554: * Returns a File preference.
555: */
556: public File getFile(String key) {
557: File defaultValue = (File) getDefault(key);
558: String sp = internal.getProperty(key);
559: if (sp == null) {
560: return defaultValue;
561: }
562: File file = new File(sp);
563: if (file.exists())
564: return file;
565: else {
566: internal.remove(key);
567: return defaultValue;
568: }
569: }
570:
571: /**
572: * Returns an array of Files preference.
573: */
574: public File[] getFiles(String mkey) {
575: File last;
576: int i = 0;
577: ArrayList v = new ArrayList();
578: while (true) {
579: last = getFile(mkey + i);
580: i++;
581: if (last == null)
582: break;
583: v.add(last);
584: }
585: if (v.size() != 0) {
586: File[] path = new File[v.size()];
587: return (File[]) v.toArray(path);
588: } else {
589: return (File[]) getDefault(mkey);
590: }
591: }
592:
593: /**
594: * Gets an int preference.
595: */
596: public int getInteger(String key) {
597: int defaultValue = 0;
598: if (getDefault(key) != null)
599: defaultValue = ((Integer) getDefault(key)).intValue();
600: String sp = internal.getProperty(key);
601: if (sp == null) {
602: return defaultValue;
603: }
604: int value;
605: try {
606: value = Integer.parseInt(sp);
607: } catch (NumberFormatException ex) {
608: internal.remove(key);
609: return defaultValue;
610: }
611: return value;
612: }
613:
614: /**
615: * Gets a float preference.
616: */
617: public float getFloat(String key) {
618: float defaultValue = 0;
619: if (getDefault(key) != null)
620: defaultValue = ((Float) getDefault(key)).floatValue();
621: String sp = internal.getProperty(key);
622: if (sp == null) {
623: return defaultValue;
624: }
625: float value;
626: try {
627: value = Float.parseFloat(sp);
628: } catch (NumberFormatException ex) {
629: setFloat(key, defaultValue);
630: return defaultValue;
631: }
632: return value;
633: }
634:
635: /**
636: * Gets a boolean preference. If not found and no default returns false.
637: */
638: public boolean getBoolean(String key) {
639: if (internal.getProperty(key) != null)
640: return (internal.getProperty(key).equals("true")) ? true
641: : false;
642: else if (getDefault(key) != null)
643: return ((Boolean) getDefault(key)).booleanValue();
644: else
645: return false;
646: }
647:
648: /**
649: * Sets a Rectangle preference. If null removes it.
650: */
651: public void setRectangle(String key, Rectangle value) {
652: if (value != null && !value.equals(getDefault(key)))
653: internal.setProperty(key, value.x + " " + value.y + " "
654: + value.width + " " + value.height);
655: else
656: internal.remove(key);
657: }
658:
659: /**
660: * Sets a Dimension preference. If null removes it.
661: */
662: public void setDimension(String key, Dimension value) {
663: if (value != null && !value.equals(getDefault(key)))
664: internal.setProperty(key, value.width + " " + value.height);
665: else
666: internal.remove(key);
667: }
668:
669: /**
670: * Sets a Point preference. If null removes it.
671: */
672: public void setPoint(String key, Point value) {
673: if (value != null && !value.equals(getDefault(key)))
674: internal.setProperty(key, value.x + " " + value.y);
675: else
676: internal.remove(key);
677: }
678:
679: /**
680: * Sets a Color preference. If null removes it.
681: */
682: public void setColor(String key, Color value) {
683: if (value != null && !value.equals(getDefault(key)))
684: internal.setProperty(key, value.getRed() + " "
685: + value.getGreen() + " " + value.getBlue() + " "
686: + value.getAlpha());
687: else
688: internal.remove(key);
689: }
690:
691: /**
692: * Sets a Font preference. If null removes it.
693: */
694: public void setFont(String key, Font value) {
695: if (value != null && !value.equals(getDefault(key)))
696: internal.setProperty(key, value.getName() + " "
697: + value.getSize() + " " + value.getStyle());
698: else
699: internal.remove(key);
700: }
701:
702: /**
703: * Sets a String preference. If null removes it.
704: */
705: public void setString(String key, String value) {
706: if (value != null && !value.equals(getDefault(key)))
707: internal.setProperty(key, value);
708: else
709: internal.remove(key);
710: }
711:
712: /**
713: * Sets a String array preference. If null or size null removes
714: * previous preference.
715: */
716: public void setStrings(String mkey, String[] values) {
717: int j = 0;
718: if (values != null)
719: for (int i = 0; i < values.length; i++) {
720: if (values[i] != null) {
721: setString(mkey + j, values[i]);
722: j++;
723: }
724: }
725: // erase other elements
726: String last;
727: while (true) {
728: last = getString(mkey + j);
729: if (last == null)
730: break;
731: setString(mkey + j, null);
732: j++;
733: }
734: }
735:
736: /**
737: * Sets an URL property. If null removes it.
738: */
739: public void setURL(String key, URL value) {
740: if (value != null && !value.equals(getDefault(key)))
741: internal.setProperty(key, value.toString());
742: else
743: internal.remove(key);
744: }
745:
746: /**
747: * Sets an array of URLs property. If null or size null removes
748: * previous preference.
749: */
750: public void setURLs(String mkey, URL[] values) {
751: int j = 0;
752: if (values != null)
753: for (int i = 0; i < values.length; i++) {
754: if (values[i] != null) {
755: setURL(mkey + j, values[i]);
756: j++;
757: }
758: }
759: // erase other elements
760: String last;
761: while (true) {
762: last = getString(mkey + j);
763: if (last == null)
764: break;
765: setString(mkey + j, null);
766: j++;
767: }
768: }
769:
770: /**
771: * Sets a File property. If null removes it.
772: */
773: public void setFile(String key, File value) {
774: if (value != null && !value.equals(getDefault(key)))
775: internal.setProperty(key, value.getAbsolutePath());
776: else
777: internal.remove(key);
778: }
779:
780: /**
781: * Sets an array of Files property. If null or size null removes
782: * previous preference.
783: */
784: public void setFiles(String mkey, File[] values) {
785: int j = 0;
786: if (values != null)
787: for (int i = 0; i < values.length; i++) {
788: if (values[i] != null) {
789: setFile(mkey + j, values[i]);
790: j++;
791: }
792: }
793: // erase other elements
794: String last;
795: while (true) {
796: last = getString(mkey + j);
797: if (last == null)
798: break;
799: setString(mkey + j, null);
800: j++;
801: }
802: }
803:
804: /**
805: * Sets an int property.
806: */
807: public void setInteger(String key, int value) {
808: if (getDefault(key) != null
809: && ((Integer) getDefault(key)).intValue() != value)
810: internal.setProperty(key, Integer.toString(value));
811: else
812: internal.remove(key);
813: }
814:
815: /**
816: * Sets a float property.
817: */
818: public void setFloat(String key, float value) {
819: if (getDefault(key) != null
820: && ((Float) getDefault(key)).floatValue() != value)
821: internal.setProperty(key, Float.toString(value));
822: else
823: internal.remove(key);
824: }
825:
826: /**
827: * Sets a boolean property.
828: */
829: public void setBoolean(String key, boolean value) {
830: if (getDefault(key) != null
831: && ((Boolean) getDefault(key)).booleanValue() != value) {
832: internal.setProperty(key, value ? "true" : "false");
833: } else {
834: internal.remove(key);
835: }
836: }
837: }
|