diff options
Diffstat (limited to 'jpa/plugins/org.eclipse.jpt.utility/src/org')
4 files changed, 741 insertions, 0 deletions
diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/BidiStringConverter.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/BidiStringConverter.java new file mode 100644 index 0000000000..fcd8f4a311 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/BidiStringConverter.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2007 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +/** + * Used by various "pluggable" classes to transform objects + * into strings and vice versa. + * + * If anyone can come up with a better class name + * and/or method name, I would love to hear it. ~bjv + */ +public interface BidiStringConverter<T> extends StringConverter<T> { + + /** + * Convert the specified string into an object. + * The semantics of "convert to object" is determined by the + * contract between the client and the server. + * Typically, if the string is null, null is returned. + */ + T convertToObject(String s); + + + final class Default<S> implements BidiStringConverter<S> { + @SuppressWarnings("unchecked") + public static final BidiStringConverter INSTANCE = new Default(); + @SuppressWarnings("unchecked") + public static <R> BidiStringConverter<R> instance() { + return INSTANCE; + } + // ensure single instance + private Default() { + super(); + } + // simply return the object's #toString() result + public String convertToString(S o) { + return (o == null) ? null : o.toString(); + } + // simply return the string + @SuppressWarnings("unchecked") + public S convertToObject(String s) { + return (S) s; + } + @Override + public String toString() { + return "BidiStringConverter.Default"; + } + } + + final class Disabled<S> implements BidiStringConverter<S> { + @SuppressWarnings("unchecked") + public static final BidiStringConverter INSTANCE = new Disabled(); + @SuppressWarnings("unchecked") + public static <R> BidiStringConverter<R> instance() { + return INSTANCE; + } + // ensure single instance + private Disabled() { + super(); + } + // throw an exception + public String convertToString(S o) { + throw new UnsupportedOperationException(); + } + // throw an exception + public S convertToObject(String s) { + throw new UnsupportedOperationException(); + } + @Override + public String toString() { + return "BidiStringConverter.Disabled"; + } + } + + final class BooleanConverter implements BidiStringConverter<Boolean> { + public static final BidiStringConverter<Boolean> INSTANCE = new BooleanConverter(); + public static BidiStringConverter<Boolean> instance() { + return INSTANCE; + } + // ensure single instance + private BooleanConverter() { + super(); + } + /** Return "true" if the Boolean is true, otherwise return "false". */ + public String convertToString(Boolean b) { + return (b == null) ? null : b.toString(); + } + /** Return Boolean.TRUE if the string is "true" (case-insensitive), otherwise return Boolean.FALSE. */ + public Boolean convertToObject(String s) { + return (s == null) ? null : Boolean.valueOf(s); + } + @Override + public String toString() { + return "BidiStringConverter.BooleanConverter"; + } + } + + final class IntegerConverter implements BidiStringConverter<Integer> { + public static final BidiStringConverter<Integer> INSTANCE = new IntegerConverter(); + public static BidiStringConverter<Integer> instance() { + return INSTANCE; + } + // ensure single instance + private IntegerConverter() { + super(); + } + /** Integer's #toString() works well. */ + public String convertToString(Integer integer) { + return (integer == null) ? null : integer.toString(); + } + /** Convert the string to an Integer, if possible. */ + public Integer convertToObject(String s) { + return (s == null) ? null : Integer.valueOf(s); + } + @Override + public String toString() { + return "BidiStringConverter.IntegerConverter"; + } + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/StringConverter.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/StringConverter.java new file mode 100644 index 0000000000..556b530862 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/StringConverter.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2007 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal; + +/** + * Used by various "pluggable" classes to transform objects + * into strings. + */ +public interface StringConverter<T> { + + /** + * Convert the specified object into a string. + * The semantics of "convert" is determined by the + * contract between the client and the server. + */ + String convertToString(T o); + + + final class Default<S> implements StringConverter<S> { + @SuppressWarnings("unchecked") + public static final StringConverter INSTANCE = new Default(); + @SuppressWarnings("unchecked") + public static <R> StringConverter<R> instance() { + return INSTANCE; + } + // ensure single instance + private Default() { + super(); + } + // simply return the object's #toString() result + public String convertToString(S o) { + return (o == null) ? null : o.toString(); + } + @Override + public String toString() { + return "StringConverter.Default"; + } + } + + final class Disabled<S> implements StringConverter<S> { + @SuppressWarnings("unchecked") + public static final StringConverter INSTANCE = new Disabled(); + @SuppressWarnings("unchecked") + public static <R> StringConverter<R> instance() { + return INSTANCE; + } + // ensure single instance + private Disabled() { + super(); + } + // throw an exception + public String convertToString(S o) { + throw new UnsupportedOperationException(); + } + @Override + public String toString() { + return "StringConverter.Disabled"; + } + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencePropertyValueModel.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencePropertyValueModel.java new file mode 100644 index 0000000000..66025000ea --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencePropertyValueModel.java @@ -0,0 +1,334 @@ +/******************************************************************************* + * Copyright (c) 2007 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.model.value.prefs; + +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; + +import org.eclipse.jpt.utility.internal.BidiStringConverter; +import org.eclipse.jpt.utility.internal.model.value.AspectAdapter; +import org.eclipse.jpt.utility.internal.model.value.PropertyValueModel; +import org.eclipse.jpt.utility.internal.model.value.ValueModel; + +/** + * This adapter wraps a Preference and converts it into a PropertyValueModel. + * It listens for the appropriate "preference" changes and converts them into + * VALUE property changes. It also allows the specification of a default value + * for the Preference, which, by default, is null (and is probably *not* a very + * good default). + * + * You can configure whether the preference's value is returned, + * unchanged, as a string or as some other object (e.g. an Integer) by + * setting the adapter's converter. Internally, the preference's value + * is stored as the converted object; and the conversions take place + * when reading or writing from the preferences node or retrieving the + * value from an event fired by the preferences node. + * + * This adapter is a bit different from most other adapters because the + * change events fired off by a Preferences node are asynchronous from + * the change itself. (AbstractPreferences uses an event dispatch daemon.) + * As a result, a client can set our value with #setValue(Object) and we + * will return from that method before we ever receive notification from + * the Preferences node that *it* has changed. This means we cannot + * rely on that event to keep our internally cached value in synch. + */ +public class PreferencePropertyValueModel + extends AspectAdapter + implements PropertyValueModel +{ + /** The key to the preference we use for the value. */ + protected String key; + + /** + * Cache the current (object) value of the preference so we + * can pass an "old value" when we fire a property change event. + */ + protected Object value; + + /** + * The default (object) value returned if there is no value + * associated with the preference. + */ + protected Object defaultValue; + + /** + * This converter is used to convert the preference's + * string value to and from an object. + */ + protected BidiStringConverter converter; + + /** A listener that listens to the appropriate preference. */ + protected PreferenceChangeListener preferenceChangeListener; + + + // ********** constructors ********** + + /** + * Construct an adapter for the specified preference. + * The default value of the preference will be null. + */ + public PreferencePropertyValueModel(Preferences preferences, String key) { + this(preferences, key, null); + } + + /** + * Construct an adapter for the specified preference with + * the specified default value for the preference. + */ + public PreferencePropertyValueModel(Preferences preferences, String key, Object defaultValue) { + super(preferences); + this.key = key; + this.defaultValue = defaultValue; + } + + /** + * Construct an adapter for the specified preference with + * the specified default value for the preference. + */ + public PreferencePropertyValueModel(Preferences preferences, String key, boolean defaultValue) { + this(preferences, key, defaultValue ? Boolean.TRUE : Boolean.FALSE); + } + + /** + * Construct an adapter for the specified preference with + * the specified default value for the preference. + */ + public PreferencePropertyValueModel(Preferences preferences, String key, int defaultValue) { + this(preferences, key, new Integer(defaultValue)); + } + + /** + * Construct an adapter for the specified preference. + * The default value of the preference will be null. + */ + public PreferencePropertyValueModel(ValueModel preferencesHolder, String key) { + this(preferencesHolder, key, null); + } + + /** + * Construct an adapter for the specified preference with + * the specified default value for the preference. + */ + public PreferencePropertyValueModel(ValueModel preferencesHolder, String key, Object defaultValue) { + super(preferencesHolder); + this.key = key; + this.defaultValue = defaultValue; + } + + + // ********** initialization ********** + + @Override + protected void initialize() { + super.initialize(); + // our value is null when we are not listening to the preference + this.value = null; + this.converter = BidiStringConverter.Default.instance(); + this.preferenceChangeListener = this.buildPreferenceChangeListener(); + } + + /** + * A preference has changed, notify the listeners if necessary. + */ + protected PreferenceChangeListener buildPreferenceChangeListener() { + // transform the preference change events into VALUE property change events + return new PreferenceChangeListener() { + public void preferenceChange(PreferenceChangeEvent e) { + PreferencePropertyValueModel.this.preferenceChanged(e.getKey(), e.getNewValue()); + } + @Override + public String toString() { + return "preference change listener"; + } + }; + } + + + // ********** ValueModel implementation ********** + + /** + * Return the cached (converted) value. + */ + public synchronized Object getValue() { + return this.value; + } + + + // ********** PropertyValueModel implementation ********** + + /** + * Set the cached value, then set the appropriate preference value. + */ + public synchronized void setValue(Object value) { + if (this.hasNoListeners()) { + return; // no changes allowed when we have no listeners + } + + Object old = this.value; + this.value = value; + this.fireAspectChange(old, value); + + if ((this.subject != null) && this.shouldSetPreference(old, value)) { + this.setValueOnSubject(value); + } + } + + + // ********** AspectAdapter implementation ********** + + @Override + protected boolean hasListeners() { + return this.hasAnyPropertyChangeListeners(VALUE); + } + + @Override + protected void fireAspectChange(Object oldValue, Object newValue) { + this.firePropertyChanged(VALUE, oldValue, newValue); + } + + @Override + protected void engageNonNullSubject() { + ((Preferences) this.subject).addPreferenceChangeListener(this.preferenceChangeListener); + this.value = this.buildValue(); + } + + @Override + protected void disengageNonNullSubject() { + try { + ((Preferences) this.subject).removePreferenceChangeListener(this.preferenceChangeListener); + } catch (IllegalStateException ex) { + // for some odd reason, we are not allowed to remove a listener from a "dead" + // preferences node; so handle the exception that gets thrown here + if ( ! ex.getMessage().equals("Node has been removed.")) { + // if it is not the expected exception, re-throw it + throw ex; + } + } + this.value = null; + } + + + // ********** AbstractModel implementation ********** + + @Override + public void toString(StringBuilder sb) { + sb.append(this.key); + sb.append(" => "); + sb.append(this.value); + } + + + // ********** public API ********** + + /** + * Return the preference's key. + */ + public String getKey() { + return this.key; + } + + /** + * Return the converter used to convert the + * preference's value to and from a string. + * The default is to use the unconverted string. + */ + public synchronized BidiStringConverter getConverter() { + return this.converter; + } + + /** + * Set the converter used to convert the + * preference's value to and from a string. + * The default is to use the unconverted string. + */ + public synchronized void setConverter(BidiStringConverter converter) { + this.converter = converter; + } + + + // ********** internal methods ********** + + /** + * Return the preference's value. + * At this point the subject may be null. + */ + protected Object buildValue() { + if (this.subject == null) { + return null; + } + return this.getValueFromSubject(); + } + + /** + * Return the appropriate preference, converted to the appropriate object. + * At this point we can be sure that the subject is not null. + */ + protected Object getValueFromSubject() { + return this.convertToObject(((Preferences) this.subject).get(this.key, this.convertToString(this.defaultValue))); + } + + /** + * Set the appropriate preference after converting the value to a string. + * At this point we can be sure that the subject is not null. + */ + protected void setValueOnSubject(Object value) { + ((Preferences) this.subject).put(this.key, this.convertToString(value)); + } + + /** + * Return whether the specified new value should be passed + * through to the preference. By default, only if the value has changed, + * will it be passed through to the preference. This also has the + * effect of not creating new preferences in the "backing store" + * if the new value is the same as the default value. + * + * Subclasses can override this method to return true if they + * would like to ALWAYS pass through the new value to the preference. + */ + protected boolean shouldSetPreference(Object oldValue, Object newValue) { + return this.attributeValueHasChanged(oldValue, newValue); + } + + /** + * Convert the specified object to a string that can be stored as + * the value of the preference. + */ + protected String convertToString(Object o) { + return this.converter.convertToString(o); + } + + /** + * Convert the specified preference value string to an + * appropriately-typed object to be returned to the client. + */ + protected Object convertToObject(String s) { + return this.converter.convertToObject(s); + } + + protected void preferenceChanged(String prefKey, String newValue) { + if (prefKey.equals(this.key)) { + this.preferenceChanged(); + } + } + + /** + * The underlying preference changed; either because we changed it + * in #setValueOnSubject(Object) or a third-party changed it. + * If this is called because of our own change, the event will be + * swallowed because the old and new values are the same. + */ + protected synchronized void preferenceChanged() { + Object old = this.value; + this.value = this.buildValue(); + this.fireAspectChange(old, this.value); + } + +} diff --git a/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencesCollectionValueModel.java b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencesCollectionValueModel.java new file mode 100644 index 0000000000..4fc5557086 --- /dev/null +++ b/jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencesCollectionValueModel.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2007 Oracle. All rights reserved. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0, which accompanies this distribution + * and is available at http://www.eclipse.org/legal/epl-v10.html. + * + * Contributors: + * Oracle - initial API and implementation + ******************************************************************************/ +package org.eclipse.jpt.utility.internal.model.value.prefs; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.prefs.BackingStoreException; +import java.util.prefs.PreferenceChangeEvent; +import java.util.prefs.PreferenceChangeListener; +import java.util.prefs.Preferences; + +import org.eclipse.jpt.utility.internal.iterators.ArrayIterator; +import org.eclipse.jpt.utility.internal.iterators.TransformationIterator; +import org.eclipse.jpt.utility.internal.model.value.AspectAdapter; +import org.eclipse.jpt.utility.internal.model.value.CollectionValueModel; +import org.eclipse.jpt.utility.internal.model.value.ValueModel; + +/** + * This adapter wraps a Preferences node and converts its preferences into a + * CollectionValueModel of PreferencePropertyValueModels. It listens for + * "preference" changes and converts them into VALUE collection changes. + */ +public class PreferencesCollectionValueModel + extends AspectAdapter + implements CollectionValueModel +{ + + /** Cache the current preferences, stored in models and keyed by name. */ + protected Map preferences; + + /** A listener that listens to the preferences node for added or removed preferences. */ + protected PreferenceChangeListener preferenceChangeListener; + + + // ********** constructors ********** + + /** + * Construct an adapter for the specified preferences node. + */ + public PreferencesCollectionValueModel(Preferences preferences) { + super(preferences); + } + + /** + * Construct an adapter for the specified preferences node. + */ + public PreferencesCollectionValueModel(ValueModel preferencesHolder) { + super(preferencesHolder); + } + + + // ********** initialization ********** + + @Override + protected void initialize() { + super.initialize(); + this.preferences = new HashMap(); + this.preferenceChangeListener = this.buildPreferenceChangeListener(); + } + + /** + * A preferences have changed, notify the listeners. + */ + protected PreferenceChangeListener buildPreferenceChangeListener() { + // transform the preference change events into VALUE collection change events + return new PreferenceChangeListener() { + public void preferenceChange(PreferenceChangeEvent e) { + PreferencesCollectionValueModel.this.preferenceChanged(e.getKey(), e.getNewValue()); + } + @Override + public String toString() { + return "preference change listener"; + } + }; + } + + + // ********** ValueModel implementation ********** + + /** + * Return an iterator on the preference models. + */ + public synchronized Object getValue() { + return this.preferences.values().iterator(); + } + + + // ********** CollectionValueModel implementation ********** + + public void addItem(Object item) { + throw new UnsupportedOperationException(); + } + + public void addItems(Collection items) { + for (Iterator stream = items.iterator(); stream.hasNext(); ) { + this.addItem(stream.next()); + } + } + + public void removeItem(Object item) { + throw new UnsupportedOperationException(); + } + + public void removeItems(Collection items) { + for (Iterator stream = items.iterator(); stream.hasNext(); ) { + this.removeItem(stream.next()); + } + } + + public synchronized int size() { + return this.preferences.size(); + } + + + // ********** AspectAdapter implementation ********** + + @Override + protected boolean hasListeners() { + return this.hasAnyCollectionChangeListeners(VALUE); + } + + @Override + protected void fireAspectChange(Object oldValue, Object newValue) { + this.fireCollectionChanged(VALUE); + } + + @Override + protected void engageNonNullSubject() { + ((Preferences) this.subject).addPreferenceChangeListener(this.preferenceChangeListener); + for (Iterator stream = this.preferenceModels(); stream.hasNext(); ) { + PreferencePropertyValueModel preferenceModel = (PreferencePropertyValueModel) stream.next(); + this.preferences.put(preferenceModel.getKey(), preferenceModel); + } + } + + @Override + protected void disengageNonNullSubject() { + try { + ((Preferences) this.subject).removePreferenceChangeListener(this.preferenceChangeListener); + } catch (IllegalStateException ex) { + // for some odd reason, we are not allowed to remove a listener from a "dead" + // preferences node; so handle the exception that gets thrown here + if ( ! ex.getMessage().equals("Node has been removed.")) { + // if it is not the expected exception, re-throw it + throw ex; + } + } + this.preferences.clear(); + } + + + // ********** AbstractModel implementation ********** + + @Override + public void toString(StringBuilder sb) { + sb.append(this.subject); + } + + + // ********** internal methods ********** + + /** + * Return an iterator on the preference models. + * At this point we can be sure that the subject is not null. + */ + protected Iterator preferenceModels() { + String[] keys; + try { + keys = ((Preferences) this.subject).keys(); + } catch (BackingStoreException ex) { + throw new RuntimeException(ex); + } + return new TransformationIterator(new ArrayIterator(keys)) { + protected Object transform(Object next) { + return PreferencesCollectionValueModel.this.buildPreferenceModel((String) next); + } + }; + } + + /** + * Override this method to tweak the model used to wrap the + * specified preference (e.g. to customize the model's converter). + */ + protected PreferencePropertyValueModel buildPreferenceModel(String key) { + return new PreferencePropertyValueModel(this.subjectHolder, key); + } + + protected synchronized void preferenceChanged(String key, String newValue) { + if (newValue == null) { + // a preference was removed + PreferencePropertyValueModel preferenceModel = (PreferencePropertyValueModel) this.preferences.remove(key); + this.fireItemRemoved(VALUE, preferenceModel); + } else if ( ! this.preferences.containsKey(key)) { + // a preference was added + PreferencePropertyValueModel preferenceModel = this.buildPreferenceModel(key); + this.preferences.put(key, preferenceModel); + this.fireItemAdded(VALUE, preferenceModel); + } else { + // a preference's value changed - do nothing + } + } + +} |