001: package net.refractions.udig.catalog;
002:
003: import java.io.IOException;
004: import java.util.HashMap;
005: import java.util.Map;
006: import java.util.Set;
007: import java.util.concurrent.CopyOnWriteArraySet;
008:
009: import org.eclipse.jface.preference.IPersistentPreferenceStore;
010: import org.eclipse.jface.preference.IPreferenceStore;
011: import org.eclipse.jface.resource.JFaceResources;
012: import org.eclipse.jface.util.Assert;
013: import org.eclipse.jface.util.IPropertyChangeListener;
014: import org.eclipse.jface.util.PropertyChangeEvent;
015: import org.eclipse.jface.util.SafeRunnable;
016:
017: /**
018: * An abstract preference store implementation for remote preference stores. This class is quite
019: * similar to <code>org.eclipse.jface.preference.PreferenceStore</code>, with the exception that
020: * it loads and saves its values from a remote source on startup and shutdown, respectively.
021: *
022: * @author chorner
023: */
024: public abstract class RemotePreferenceStore implements
025: IPersistentPreferenceStore {
026:
027: /**
028: * List of registered listeners (element type:
029: * <code>IPropertyChangeListener</code>). These listeners are to be
030: * informed when the current value of a preference changes.
031: */
032: private Set<IPropertyChangeListener> listeners = new CopyOnWriteArraySet<IPropertyChangeListener>();
033:
034: /**
035: * The locally stored copy of the map from preference name to preference value
036: * (represented as strings).
037: */
038: private Map<String, String> localProperties;
039:
040: /**
041: * The mapping from preference name to default preference value (represented
042: * as strings); <code>null</code> if none.
043: */
044: private Map<String, String> defaultProperties;
045:
046: /**
047: * Indicates whether a value as been changed by <code>setToDefault</code>
048: * or <code>setValue</code>; initially <code>false</code>.
049: */
050: private boolean dirty = false;
051:
052: /**
053: * Indicates that the remote store has been initially contacted.
054: */
055: private boolean ready = false;
056:
057: /**
058: * Obtains the remote value for the specified preference. Subclasses must implement.
059: *
060: * @param name key
061: * @return String value
062: */
063: protected abstract String getValue(String name);
064:
065: /**
066: * Stores a remote value for the preference with the specified key. Subclasses must implement.
067: */
068: public abstract void putValue(String name, String value);
069:
070: /**
071: * Determines if the key specified exists in the remote store.
072: *
073: * @param name key of the preference
074: * @return true if key exists
075: */
076: public abstract boolean isKey(String name);
077:
078: public RemotePreferenceStore() {
079: defaultProperties = new HashMap<String, String>();
080: localProperties = new HashMap<String, String>();
081: }
082:
083: /**
084: * This is the initial hit of the remote store. All methods that read or write will check to
085: * ensure we've interacted with the store prior to doing anything.
086: */
087: protected void load() {
088: //grab all the preferences (initial load so we can detect local modifications)
089: String[] names = preferenceNames();
090: for (int i = 0; i < names.length; i++) {
091: String value = getValue(names[i]);
092: if (value != null) {
093: localProperties.put(names[i], value);
094: }
095: }
096: ready = true;
097: }
098:
099: public void save() throws IOException {
100: String[] names = preferenceNames();
101: for (int i = 0; i < names.length; i++) {
102: String remoteValue = getValue(names[i]);
103: String localValue = toString(localProperties.get(names[i]));
104: String defaultValue = toString(defaultProperties
105: .get(names[i]));
106: if (localValue == null) {
107: if (defaultValue == null) {
108: throw new IOException(
109: "Default property value not defined"); //$NON-NLS-1$
110: } else {
111: putValue(names[i], defaultValue); //revert to default
112: }
113: } else if (remoteValue == null) {
114: putValue(names[i], localValue); //first save
115: } else {
116: if (remoteValue.equals(localValue)) {
117: putValue(names[i], localValue); //regular save
118: }
119: }
120: }
121: dirty = false;
122: }
123:
124: /**
125: * Had to add this method as the Property object was being unreliable in returning its contents
126: * using the getProperty method. We also want null to be null, rather than not "null".
127: */
128: private String toString(Object object) {
129: if (object == null)
130: return null;
131: if (object instanceof Boolean) {
132: if (((Boolean) object).booleanValue()) {
133: return IPreferenceStore.TRUE;
134: } else {
135: return IPreferenceStore.FALSE;
136: }
137: }
138: return String.valueOf(object);
139: }
140:
141: public void addPropertyChangeListener(
142: IPropertyChangeListener listener) {
143: listeners.add(listener);
144: }
145:
146: public boolean contains(String name) {
147: if (name == null)
148: return false;
149: if (localProperties.containsKey(name)
150: || defaultProperties.containsKey(name))
151: return true;
152: // check the remote store too
153: Object remoteValue = getValue(name);
154: if (remoteValue == null)
155: return false;
156: else
157: return true;
158: }
159:
160: public void firePropertyChangeEvent(String name, Object oldValue,
161: Object newValue) {
162: // Do we need to fire an event.
163: if ((oldValue == null || !oldValue.equals(newValue))) {
164: final PropertyChangeEvent pe = new PropertyChangeEvent(
165: this , name, oldValue, newValue);
166: for (final IPropertyChangeListener l : listeners) {
167: SafeRunnable.run(new SafeRunnable(JFaceResources
168: .getString("PreferenceStore.changeError")) { //$NON-NLS-1$
169: public void run() {
170: l.propertyChange(pe);
171: }
172: });
173: }
174: }
175: }
176:
177: public boolean getBoolean(String name) {
178: if (!ready)
179: load();
180: return getBoolean(localProperties, name);
181: }
182:
183: private boolean getBoolean(Map<String, String> p, String name) {
184: String value = p != null ? p.get(name) : null;
185: if (value == null)
186: return BOOLEAN_DEFAULT_DEFAULT;
187: if (value.equals(IPreferenceStore.TRUE))
188: return true;
189: return false;
190: }
191:
192: public boolean getDefaultBoolean(String name) {
193: return getBoolean(defaultProperties, name);
194: }
195:
196: public double getDefaultDouble(String name) {
197: return getDouble(defaultProperties, name);
198: }
199:
200: private double getDouble(Map<String, String> p, String name) {
201: String value = p != null ? p.get(name) : null;
202: if (value == null)
203: return DOUBLE_DEFAULT_DEFAULT;
204: double ival = DOUBLE_DEFAULT_DEFAULT;
205: try {
206: ival = new Double(value).doubleValue();
207: } catch (NumberFormatException e) {
208: }
209: return ival;
210: }
211:
212: public float getDefaultFloat(String name) {
213: return getFloat(defaultProperties, name);
214: }
215:
216: private float getFloat(Map<String, String> p, String name) {
217: String value = p != null ? p.get(name) : null;
218: if (value == null)
219: return FLOAT_DEFAULT_DEFAULT;
220: float ival = FLOAT_DEFAULT_DEFAULT;
221: try {
222: ival = new Float(value).floatValue();
223: } catch (NumberFormatException e) {
224: }
225: return ival;
226: }
227:
228: public int getDefaultInt(String name) {
229: return getInt(defaultProperties, name);
230: }
231:
232: private int getInt(Map<String, String> p, String name) {
233: String value = p != null ? p.get(name) : null;
234: if (value == null)
235: return INT_DEFAULT_DEFAULT;
236: int ival = 0;
237: try {
238: ival = Integer.parseInt(value);
239: } catch (NumberFormatException e) {
240: }
241: return ival;
242: }
243:
244: public long getDefaultLong(String name) {
245: return getLong(defaultProperties, name);
246: }
247:
248: private long getLong(Map<String, String> p, String name) {
249: String value = p != null ? p.get(name) : null;
250: if (value == null)
251: return LONG_DEFAULT_DEFAULT;
252: long ival = LONG_DEFAULT_DEFAULT;
253: try {
254: ival = Long.parseLong(value);
255: } catch (NumberFormatException e) {
256: }
257: return ival;
258: }
259:
260: public String getDefaultString(String name) {
261: return getString(defaultProperties, name);
262: }
263:
264: private String getString(Map<String, String> p, String name) {
265: String value = p != null ? p.get(name) : null;
266: if (value == null)
267: return STRING_DEFAULT_DEFAULT;
268: return value;
269: }
270:
271: public double getDouble(String name) {
272: if (!ready)
273: load();
274: return getDouble(localProperties, name);
275: }
276:
277: public float getFloat(String name) {
278: if (!ready)
279: load();
280: return getFloat(localProperties, name);
281: }
282:
283: public int getInt(String name) {
284: if (!ready)
285: load();
286: return getInt(localProperties, name);
287: }
288:
289: public long getLong(String name) {
290: if (!ready)
291: load();
292: return getLong(localProperties, name);
293: }
294:
295: public String getString(String name) {
296: if (!ready)
297: load();
298: return getString(localProperties, name);
299: }
300:
301: public boolean isDefault(String name) {
302: //does not check remote store
303: return (!localProperties.containsKey(name) && defaultProperties
304: .containsKey(name));
305: }
306:
307: public boolean needsSaving() {
308: if (!ready)
309: load();
310: return dirty;
311: }
312:
313: /**
314: * Returns an enumeration of all preferences known to this store which have
315: * current values other than their default value.
316: *
317: * @return an array of preference names
318: */
319: public String[] preferenceNames() {
320: Set<String> names = defaultProperties.keySet();
321: return (String[]) names.toArray(new String[names.size()]);
322: }
323:
324: public void removePropertyChangeListener(
325: IPropertyChangeListener listener) {
326: listeners.remove(listener);
327: }
328:
329: public void setDefault(String name, double value) {
330: setValue(defaultProperties, name, value);
331: }
332:
333: public void setDefault(String name, float value) {
334: setValue(defaultProperties, name, value);
335: }
336:
337: public void setDefault(String name, int value) {
338: setValue(defaultProperties, name, value);
339: }
340:
341: public void setDefault(String name, long value) {
342: setValue(defaultProperties, name, value);
343: }
344:
345: public void setDefault(String name, String value) {
346: setValue(defaultProperties, name, value);
347: }
348:
349: public void setDefault(String name, boolean value) {
350: setValue(defaultProperties, name, value);
351: }
352:
353: private void setValue(Map<String, String> p, String name,
354: double value) {
355: Assert.isTrue(p != null && name != null);
356: p.put(name, toString(new Double(value)));
357: }
358:
359: private void setValue(Map<String, String> p, String name,
360: float value) {
361: Assert.isTrue(p != null && name != null);
362: p.put(name, toString(new Float(value)));
363: }
364:
365: private void setValue(Map<String, String> p, String name, int value) {
366: Assert.isTrue(p != null && name != null);
367: p.put(name, toString(Integer.valueOf(value)));
368: }
369:
370: private void setValue(Map<String, String> p, String name, long value) {
371: Assert.isTrue(p != null && name != null);
372: p.put(name, toString(Long.valueOf(value)));
373: }
374:
375: private void setValue(Map<String, String> p, String name,
376: String value) {
377: Assert.isTrue(p != null && name != null && value != null);
378: p.put(name, value);
379: }
380:
381: private void setValue(Map<String, String> p, String name,
382: boolean value) {
383: Assert.isTrue(p != null && name != null);
384: p.put(name, toString(Boolean.valueOf(value)));
385: }
386:
387: public void setToDefault(String name) {
388: Object oldValue = localProperties.get(name);
389: localProperties.remove(name);
390: dirty = true;
391: Object newValue = null;
392: if (defaultProperties != null)
393: newValue = defaultProperties.get(name);
394: firePropertyChangeEvent(name, oldValue, newValue);
395: }
396:
397: public void setValue(String name, double value) {
398: double oldValue = getDouble(name);
399: if (oldValue != value) {
400: setValue(localProperties, name, value);
401: dirty = true;
402: firePropertyChangeEvent(name, new Double(oldValue),
403: new Double(value));
404: }
405: }
406:
407: public void setValue(String name, float value) {
408: float oldValue = getFloat(name);
409: if (oldValue != value) {
410: setValue(localProperties, name, value);
411: dirty = true;
412: firePropertyChangeEvent(name, new Float(oldValue),
413: new Float(value));
414: }
415: }
416:
417: public void setValue(String name, int value) {
418: int oldValue = getInt(name);
419: if (oldValue != value) {
420: setValue(localProperties, name, value);
421: dirty = true;
422: firePropertyChangeEvent(name, Integer.valueOf(oldValue),
423: Integer.valueOf(value));
424: }
425: }
426:
427: public void setValue(String name, long value) {
428: long oldValue = getLong(name);
429: if (oldValue != value) {
430: setValue(localProperties, name, value);
431: dirty = true;
432: firePropertyChangeEvent(name, Long.valueOf(oldValue), Long
433: .valueOf(value));
434: }
435: }
436:
437: public void setValue(String name, String value) {
438: String oldValue = getString(name);
439: if (oldValue == null || !oldValue.equals(value)) {
440: setValue(localProperties, name, value);
441: dirty = true;
442: firePropertyChangeEvent(name, oldValue, value);
443: }
444: }
445:
446: public void setValue(String name, boolean value) {
447: boolean oldValue = getBoolean(name);
448: if (oldValue != value) {
449: setValue(localProperties, name, value);
450: dirty = true;
451: firePropertyChangeEvent(name, Boolean.valueOf(oldValue),
452: Boolean.valueOf(value));
453: }
454: }
455:
456: }
|