001: /*******************************************************************************
002: * Copyright (c) 2005, 2006 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.ui.internal.preferences;
011:
012: import java.util.Arrays;
013: import java.util.HashMap;
014: import java.util.HashSet;
015: import java.util.Iterator;
016: import java.util.Map;
017:
018: import org.eclipse.core.commands.common.EventManager;
019: import org.eclipse.core.runtime.preferences.IEclipsePreferences;
020: import org.eclipse.core.runtime.preferences.IPreferenceNodeVisitor;
021: import org.osgi.service.prefs.BackingStoreException;
022: import org.osgi.service.prefs.Preferences;
023:
024: /**
025: * Represents a working copy of a preference node, backed by the real node.
026: * <p>
027: * Note: Working copy nodes do not fire node change events.
028: * </p>
029: * <p>
030: * Note: Preference change listeners registered on this node will only receive
031: * events from this node and not events based on the original backing node.
032: * </p>
033: * @since 3.1
034: */
035: public class WorkingCopyPreferences extends EventManager implements
036: IEclipsePreferences {
037:
038: private static final String TRUE = "true"; //$NON-NLS-1$
039:
040: private final Map temporarySettings;
041: private final IEclipsePreferences original;
042: private boolean removed = false;
043: private org.eclipse.ui.preferences.WorkingCopyManager manager;
044:
045: /**
046: * @param original the underlying preference node
047: * @param manager the working copy manager
048: */
049: public WorkingCopyPreferences(IEclipsePreferences original,
050: org.eclipse.ui.preferences.WorkingCopyManager manager) {
051: super ();
052: this .original = original;
053: this .manager = manager;
054: this .temporarySettings = new HashMap();
055: }
056:
057: /*
058: * Convenience method for throwing an exception when methods
059: * are called on a removed node.
060: */
061: private void checkRemoved() {
062: if (removed) {
063: String message = "Preference node: " + absolutePath() + " has been removed."; //$NON-NLS-1$ //$NON-NLS-2$
064: throw new IllegalStateException(message);
065: }
066: }
067:
068: /* (non-Javadoc)
069: * @see org.eclipse.core.runtime.preferences.IEclipsePreferences#addNodeChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener)
070: */
071: public void addNodeChangeListener(INodeChangeListener listener) {
072: // no-op - working copy nodes don't fire node change events
073: }
074:
075: /* (non-Javadoc)
076: * @see org.eclipse.core.runtime.preferences.IEclipsePreferences#removeNodeChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.INodeChangeListener)
077: */
078: public void removeNodeChangeListener(INodeChangeListener listener) {
079: // no-op - working copy nodes don't fire node change events
080: }
081:
082: /* (non-Javadoc)
083: * @see org.eclipse.core.runtime.preferences.IEclipsePreferences#addPreferenceChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener)
084: */
085: public void addPreferenceChangeListener(
086: IPreferenceChangeListener listener) {
087: checkRemoved();
088: addListenerObject(listener);
089: }
090:
091: /* (non-Javadoc)
092: * @see org.eclipse.core.runtime.preferences.IEclipsePreferences#removePreferenceChangeListener(org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener)
093: */
094: public void removePreferenceChangeListener(
095: IPreferenceChangeListener listener) {
096: checkRemoved();
097: removeListenerObject(listener);
098: }
099:
100: /* (non-Javadoc)
101: * @see org.osgi.service.prefs.Preferences#removeNode()
102: */
103: public void removeNode() throws BackingStoreException {
104: checkRemoved();
105:
106: // clear all values (long way so people get notified)
107: String[] keys = keys();
108: for (int i = 0; i < keys.length; i++) {
109: remove(keys[i]);
110: }
111:
112: // remove children
113: String[] childNames = childrenNames();
114: for (int i = 0; i < childNames.length; i++) {
115: node(childNames[i]).removeNode();
116: }
117:
118: // mark as removed
119: removed = true;
120: }
121:
122: /* (non-Javadoc)
123: * @see org.osgi.service.prefs.Preferences#node(java.lang.String)
124: */
125: public Preferences node(String path) {
126: checkRemoved();
127: return manager
128: .getWorkingCopy((IEclipsePreferences) getOriginal()
129: .node(path));
130: }
131:
132: /* (non-Javadoc)
133: * @see org.eclipse.core.runtime.preferences.IEclipsePreferences#accept(org.eclipse.core.runtime.preferences.IPreferenceNodeVisitor)
134: */
135: public void accept(IPreferenceNodeVisitor visitor)
136: throws BackingStoreException {
137: checkRemoved();
138: if (!visitor.visit(this )) {
139: return;
140: }
141: String[] childNames = childrenNames();
142: for (int i = 0; i < childNames.length; i++) {
143: ((IEclipsePreferences) node(childNames[i])).accept(visitor);
144: }
145: }
146:
147: /* (non-Javadoc)
148: * @see org.osgi.service.prefs.Preferences#put(java.lang.String, java.lang.String)
149: */
150: public void put(String key, String value) {
151: checkRemoved();
152: if (key == null || value == null) {
153: throw new NullPointerException();
154: }
155: String oldValue = null;
156: if (temporarySettings.containsKey(key)) {
157: oldValue = (String) temporarySettings.get(key);
158: } else {
159: oldValue = getOriginal().get(key, null);
160: }
161: temporarySettings.put(key, value);
162: if (!value.equals(oldValue)) {
163: firePropertyChangeEvent(key, oldValue, value);
164: }
165: }
166:
167: private void firePropertyChangeEvent(String key, Object oldValue,
168: Object newValue) {
169: Object[] listeners = getListeners();
170: if (listeners.length == 0) {
171: return;
172: }
173: PreferenceChangeEvent event = new PreferenceChangeEvent(this ,
174: key, oldValue, newValue);
175: for (int i = 0; i < listeners.length; i++) {
176: ((IPreferenceChangeListener) listeners[i])
177: .preferenceChange(event);
178: }
179: }
180:
181: /* (non-Javadoc)
182: * @see org.osgi.service.prefs.Preferences#get(java.lang.String, java.lang.String)
183: */
184: public String get(String key, String defaultValue) {
185: checkRemoved();
186: return internalGet(key, defaultValue);
187: }
188:
189: private String internalGet(String key, String defaultValue) {
190: if (key == null) {
191: throw new NullPointerException();
192: }
193: if (temporarySettings.containsKey(key)) {
194: Object value = temporarySettings.get(key);
195: return value == null ? defaultValue : (String) value;
196: }
197: return getOriginal().get(key, defaultValue);
198: }
199:
200: /* (non-Javadoc)
201: * @see org.osgi.service.prefs.Preferences#remove(java.lang.String)
202: */
203: public void remove(String key) {
204: checkRemoved();
205: if (key == null) {
206: throw new NullPointerException();
207: }
208: Object oldValue = null;
209: if (temporarySettings.containsKey(key)) {
210: oldValue = temporarySettings.get(key);
211: } else {
212: oldValue = original.get(key, null);
213: }
214: if (oldValue == null) {
215: return;
216: }
217: temporarySettings.put(key, null);
218: firePropertyChangeEvent(key, oldValue, null);
219: }
220:
221: /* (non-Javadoc)
222: * @see org.osgi.service.prefs.Preferences#clear()
223: */
224: public void clear() {
225: checkRemoved();
226: for (Iterator i = temporarySettings.keySet().iterator(); i
227: .hasNext();) {
228: String key = (String) i.next();
229: Object value = temporarySettings.get(key);
230: if (value != null) {
231: temporarySettings.put(key, null);
232: firePropertyChangeEvent(key, value, null);
233: }
234: }
235: }
236:
237: /* (non-Javadoc)
238: * @see org.osgi.service.prefs.Preferences#putInt(java.lang.String, int)
239: */
240: public void putInt(String key, int value) {
241: checkRemoved();
242: if (key == null) {
243: throw new NullPointerException();
244: }
245: String oldValue = null;
246: if (temporarySettings.containsKey(key)) {
247: oldValue = (String) temporarySettings.get(key);
248: } else {
249: oldValue = getOriginal().get(key, null);
250: }
251: String newValue = Integer.toString(value);
252: temporarySettings.put(key, newValue);
253: if (!newValue.equals(oldValue)) {
254: firePropertyChangeEvent(key, oldValue, newValue);
255: }
256: }
257:
258: /* (non-Javadoc)
259: * @see org.osgi.service.prefs.Preferences#getInt(java.lang.String, int)
260: */
261: public int getInt(String key, int defaultValue) {
262: checkRemoved();
263: String value = internalGet(key, null);
264: int result = defaultValue;
265: if (value != null) {
266: try {
267: result = Integer.parseInt(value);
268: } catch (NumberFormatException e) {
269: // use default
270: }
271: }
272: return result;
273: }
274:
275: /* (non-Javadoc)
276: * @see org.osgi.service.prefs.Preferences#putLong(java.lang.String, long)
277: */
278: public void putLong(String key, long value) {
279: checkRemoved();
280: if (key == null) {
281: throw new NullPointerException();
282: }
283: String oldValue = null;
284: if (temporarySettings.containsKey(key)) {
285: oldValue = (String) temporarySettings.get(key);
286: } else {
287: oldValue = getOriginal().get(key, null);
288: }
289: String newValue = Long.toString(value);
290: temporarySettings.put(key, newValue);
291: if (!newValue.equals(oldValue)) {
292: firePropertyChangeEvent(key, oldValue, newValue);
293: }
294: }
295:
296: /* (non-Javadoc)
297: * @see org.osgi.service.prefs.Preferences#getLong(java.lang.String, long)
298: */
299: public long getLong(String key, long defaultValue) {
300: checkRemoved();
301: String value = internalGet(key, null);
302: long result = defaultValue;
303: if (value != null) {
304: try {
305: result = Long.parseLong(value);
306: } catch (NumberFormatException e) {
307: // use default
308: }
309: }
310: return result;
311: }
312:
313: /* (non-Javadoc)
314: * @see org.osgi.service.prefs.Preferences#putBoolean(java.lang.String, boolean)
315: */
316: public void putBoolean(String key, boolean value) {
317: checkRemoved();
318: if (key == null) {
319: throw new NullPointerException();
320: }
321: String oldValue = null;
322: if (temporarySettings.containsKey(key)) {
323: oldValue = (String) temporarySettings.get(key);
324: } else {
325: oldValue = getOriginal().get(key, null);
326: }
327: String newValue = String.valueOf(value);
328: temporarySettings.put(key, newValue);
329: if (!newValue.equalsIgnoreCase(oldValue)) {
330: firePropertyChangeEvent(key, oldValue, newValue);
331: }
332: }
333:
334: /* (non-Javadoc)
335: * @see org.osgi.service.prefs.Preferences#getBoolean(java.lang.String, boolean)
336: */
337: public boolean getBoolean(String key, boolean defaultValue) {
338: checkRemoved();
339: String value = internalGet(key, null);
340: return value == null ? defaultValue : TRUE
341: .equalsIgnoreCase(value);
342: }
343:
344: /* (non-Javadoc)
345: * @see org.osgi.service.prefs.Preferences#putFloat(java.lang.String, float)
346: */
347: public void putFloat(String key, float value) {
348: checkRemoved();
349: if (key == null) {
350: throw new NullPointerException();
351: }
352: String oldValue = null;
353: if (temporarySettings.containsKey(key)) {
354: oldValue = (String) temporarySettings.get(key);
355: } else {
356: oldValue = getOriginal().get(key, null);
357: }
358: String newValue = Float.toString(value);
359: temporarySettings.put(key, newValue);
360: if (!newValue.equals(oldValue)) {
361: firePropertyChangeEvent(key, oldValue, newValue);
362: }
363: }
364:
365: /* (non-Javadoc)
366: * @see org.osgi.service.prefs.Preferences#getFloat(java.lang.String, float)
367: */
368: public float getFloat(String key, float defaultValue) {
369: checkRemoved();
370: String value = internalGet(key, null);
371: float result = defaultValue;
372: if (value != null) {
373: try {
374: result = Float.parseFloat(value);
375: } catch (NumberFormatException e) {
376: // use default
377: }
378: }
379: return result;
380: }
381:
382: /* (non-Javadoc)
383: * @see org.osgi.service.prefs.Preferences#putDouble(java.lang.String, double)
384: */
385: public void putDouble(String key, double value) {
386: checkRemoved();
387: if (key == null) {
388: throw new NullPointerException();
389: }
390: String oldValue = null;
391: if (temporarySettings.containsKey(key)) {
392: oldValue = (String) temporarySettings.get(key);
393: } else {
394: oldValue = getOriginal().get(key, null);
395: }
396: String newValue = Double.toString(value);
397: temporarySettings.put(key, newValue);
398: if (!newValue.equals(oldValue)) {
399: firePropertyChangeEvent(key, oldValue, newValue);
400: }
401: }
402:
403: /* (non-Javadoc)
404: * @see org.osgi.service.prefs.Preferences#getDouble(java.lang.String, double)
405: */
406: public double getDouble(String key, double defaultValue) {
407: checkRemoved();
408: String value = internalGet(key, null);
409: double result = defaultValue;
410: if (value != null) {
411: try {
412: result = Double.parseDouble(value);
413: } catch (NumberFormatException e) {
414: // use default
415: }
416: }
417: return result;
418: }
419:
420: /* (non-Javadoc)
421: * @see org.osgi.service.prefs.Preferences#putByteArray(java.lang.String, byte[])
422: */
423: public void putByteArray(String key, byte[] value) {
424: checkRemoved();
425: if (key == null || value == null) {
426: throw new NullPointerException();
427: }
428: String oldValue = null;
429: if (temporarySettings.containsKey(key)) {
430: oldValue = (String) temporarySettings.get(key);
431: } else {
432: oldValue = getOriginal().get(key, null);
433: }
434: String newValue = new String(Base64.encode(value));
435: temporarySettings.put(key, newValue);
436: if (!newValue.equals(oldValue)) {
437: firePropertyChangeEvent(key, oldValue, newValue);
438: }
439: }
440:
441: /* (non-Javadoc)
442: * @see org.osgi.service.prefs.Preferences#getByteArray(java.lang.String, byte[])
443: */
444: public byte[] getByteArray(String key, byte[] defaultValue) {
445: checkRemoved();
446: String value = internalGet(key, null);
447: return value == null ? defaultValue : Base64.decode(value
448: .getBytes());
449: }
450:
451: /* (non-Javadoc)
452: * @see org.osgi.service.prefs.Preferences#keys()
453: */
454: public String[] keys() throws BackingStoreException {
455: checkRemoved();
456: HashSet allKeys = new HashSet(Arrays.asList(getOriginal()
457: .keys()));
458: allKeys.addAll(temporarySettings.keySet());
459: return (String[]) allKeys.toArray(new String[allKeys.size()]);
460: }
461:
462: /* (non-Javadoc)
463: * @see org.osgi.service.prefs.Preferences#childrenNames()
464: */
465: public String[] childrenNames() throws BackingStoreException {
466: checkRemoved();
467: return getOriginal().childrenNames();
468: }
469:
470: /* (non-Javadoc)
471: * @see org.osgi.service.prefs.Preferences#parent()
472: */
473: public Preferences parent() {
474: checkRemoved();
475: return manager
476: .getWorkingCopy((IEclipsePreferences) getOriginal()
477: .parent());
478: }
479:
480: /* (non-Javadoc)
481: * @see org.osgi.service.prefs.Preferences#nodeExists(java.lang.String)
482: */
483: public boolean nodeExists(String pathName)
484: throws BackingStoreException {
485: // short circuit for this node
486: if (pathName.length() == 0) {
487: return removed ? false : getOriginal().nodeExists(pathName);
488: }
489: return getOriginal().nodeExists(pathName);
490: }
491:
492: /* (non-Javadoc)
493: * @see org.osgi.service.prefs.Preferences#name()
494: */
495: public String name() {
496: return getOriginal().name();
497: }
498:
499: /* (non-Javadoc)
500: * @see org.osgi.service.prefs.Preferences#absolutePath()
501: */
502: public String absolutePath() {
503: return getOriginal().absolutePath();
504: }
505:
506: /* (non-Javadoc)
507: * @see org.osgi.service.prefs.Preferences#flush()
508: */
509: public void flush() throws BackingStoreException {
510: if (removed) {
511: getOriginal().removeNode();
512: return;
513: }
514: checkRemoved();
515: // update underlying preferences
516: for (Iterator i = temporarySettings.keySet().iterator(); i
517: .hasNext();) {
518: String key = (String) i.next();
519: String value = (String) temporarySettings.get(key);
520: if (value == null) {
521: getOriginal().remove(key);
522: } else {
523: getOriginal().put(key, value);
524: }
525: }
526: // clear our settings
527: temporarySettings.clear();
528:
529: // save the underlying preference store
530: getOriginal().flush();
531: }
532:
533: /* (non-Javadoc)
534: * @see org.osgi.service.prefs.Preferences#sync()
535: */
536: public void sync() throws BackingStoreException {
537: checkRemoved();
538: // forget our settings
539: temporarySettings.clear();
540: // load the underlying preference store
541: getOriginal().sync();
542: }
543:
544: /**
545: * @return Returns the original preference node.
546: */
547: private IEclipsePreferences getOriginal() {
548: return original;
549: }
550: }
|