Skip to main content

This CGIT instance is deprecated, and repositories have been moved to Gitlab or Github. See the repository descriptions for specific locations.

summaryrefslogtreecommitdiffstats
path: root/jpa
diff options
context:
space:
mode:
Diffstat (limited to 'jpa')
-rw-r--r--jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/BidiStringConverter.java127
-rw-r--r--jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/StringConverter.java68
-rw-r--r--jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencePropertyValueModel.java334
-rw-r--r--jpa/plugins/org.eclipse.jpt.utility/src/org/eclipse/jpt/utility/internal/model/value/prefs/PreferencesCollectionValueModel.java212
-rw-r--r--jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/JptUtilityModelValueTests.java4
-rw-r--r--jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/JptUtilityModelValuePrefsTests.java31
-rw-r--r--jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencePropertyValueModelTests.java377
-rw-r--r--jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesCollectionValueModelTests.java272
-rw-r--r--jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesTestCase.java85
9 files changed, 1508 insertions, 2 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
+ }
+ }
+
+}
diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/JptUtilityModelValueTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/JptUtilityModelValueTests.java
index 822702cc7a..0aa607da29 100644
--- a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/JptUtilityModelValueTests.java
+++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/JptUtilityModelValueTests.java
@@ -9,7 +9,7 @@
******************************************************************************/
package org.eclipse.jpt.utility.tests.internal.model.value;
-//import org.eclipse.jpt.utility.tests.internal.model.value.prefs.JptUtilityModelValuePrefsTests;
+import org.eclipse.jpt.utility.tests.internal.model.value.prefs.JptUtilityModelValuePrefsTests;
//import org.eclipse.jpt.utility.tests.internal.model.value.swing.JptUtilityModelValueSwingTests;
import junit.framework.Test;
@@ -20,7 +20,7 @@ public class JptUtilityModelValueTests {
public static Test suite() {
TestSuite suite = new TestSuite(JptUtilityModelValueTests.class.getPackage().getName());
-// suite.addTest(JptUtilityModelValuePrefsTests.suite());
+ suite.addTest(JptUtilityModelValuePrefsTests.suite());
// suite.addTest(JptUtilityModelValueSwingTests.suite());
suite.addTestSuite(BufferedPropertyValueModelTests.class);
diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/JptUtilityModelValuePrefsTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/JptUtilityModelValuePrefsTests.java
new file mode 100644
index 0000000000..043da07fbf
--- /dev/null
+++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/JptUtilityModelValuePrefsTests.java
@@ -0,0 +1,31 @@
+/*******************************************************************************
+ * 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.tests.internal.model.value.prefs;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+
+public class JptUtilityModelValuePrefsTests {
+
+ public static Test suite() {
+ TestSuite suite = new TestSuite(JptUtilityModelValuePrefsTests.class.getPackage().getName());
+
+ suite.addTestSuite(PreferencesCollectionValueModelTests.class);
+ suite.addTestSuite(PreferencePropertyValueModelTests.class);
+
+ return suite;
+ }
+
+ private JptUtilityModelValuePrefsTests() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencePropertyValueModelTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencePropertyValueModelTests.java
new file mode 100644
index 0000000000..4052895627
--- /dev/null
+++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencePropertyValueModelTests.java
@@ -0,0 +1,377 @@
+/*******************************************************************************
+ * 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.tests.internal.model.value.prefs;
+
+import java.util.prefs.NodeChangeEvent;
+import java.util.prefs.NodeChangeListener;
+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.ClassTools;
+import org.eclipse.jpt.utility.internal.model.event.PropertyChangeEvent;
+import org.eclipse.jpt.utility.internal.model.listener.PropertyChangeListener;
+import org.eclipse.jpt.utility.internal.model.value.PropertyValueModel;
+import org.eclipse.jpt.utility.internal.model.value.SimplePropertyValueModel;
+import org.eclipse.jpt.utility.internal.model.value.ValueModel;
+import org.eclipse.jpt.utility.internal.model.value.prefs.PreferencePropertyValueModel;
+
+public class PreferencePropertyValueModelTests extends PreferencesTestCase {
+ private PropertyValueModel nodeHolder;
+ PreferencePropertyValueModel preferenceAdapter;
+ PropertyChangeEvent event;
+ PropertyChangeListener listener;
+ boolean listenerRemoved = false;
+ PreferenceChangeEvent preferenceEvent;
+ private static final String KEY_NAME = "foo";
+ private static final String STRING_VALUE = "original string value";
+
+ public PreferencePropertyValueModelTests(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.testNode.put(KEY_NAME, STRING_VALUE);
+
+ this.nodeHolder = new SimplePropertyValueModel(this.testNode);
+ this.preferenceAdapter = new PreferencePropertyValueModel(this.nodeHolder, KEY_NAME);
+ this.listener = this.buildValueChangeListener();
+ this.preferenceAdapter.addPropertyChangeListener(ValueModel.VALUE, this.listener);
+ this.event = null;
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private PropertyChangeListener buildValueChangeListener() {
+ return new PropertyChangeListener() {
+ public void propertyChanged(PropertyChangeEvent e) {
+ if (PreferencePropertyValueModelTests.this.event != null) {
+ throw new IllegalStateException("unexpected this.event: " + e);
+ }
+ PreferencePropertyValueModelTests.this.event = e;
+ }
+ };
+ }
+
+ public void testSubjectHolder() throws Exception {
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ assertNull(this.event);
+
+ String ANOTHER_STRING_VALUE = "some other value";
+ Preferences anotherNode = this.classNode.node("another test node");
+ anotherNode.put(KEY_NAME, ANOTHER_STRING_VALUE);
+
+ this.nodeHolder.setValue(anotherNode);
+ this.verifyEvent(STRING_VALUE, ANOTHER_STRING_VALUE);
+ assertEquals(ANOTHER_STRING_VALUE, this.preferenceAdapter.getValue());
+
+ this.event = null;
+ this.nodeHolder.setValue(null);
+ this.verifyEvent(ANOTHER_STRING_VALUE, null);
+ assertNull(this.preferenceAdapter.getValue());
+
+ this.event = null;
+ this.nodeHolder.setValue(this.testNode);
+ this.verifyEvent(null, STRING_VALUE);
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ }
+
+ public void testPreferenceChange() throws Exception {
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ assertNull(this.event);
+
+ this.testNode.put(KEY_NAME, STRING_VALUE + STRING_VALUE);
+ this.waitForEventQueueToClear();
+ this.verifyEvent(STRING_VALUE, STRING_VALUE + STRING_VALUE);
+ assertEquals(STRING_VALUE + STRING_VALUE, this.preferenceAdapter.getValue());
+
+ this.event = null;
+ this.testNode.remove(KEY_NAME);
+ this.waitForEventQueueToClear();
+ this.verifyEvent(STRING_VALUE + STRING_VALUE, null);
+ assertNull(this.preferenceAdapter.getValue());
+
+ this.event = null;
+ this.testNode.put(KEY_NAME, STRING_VALUE);
+ this.waitForEventQueueToClear();
+ this.verifyEvent(null, STRING_VALUE);
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ }
+
+ public void testGetValue() throws Exception {
+ assertEquals(STRING_VALUE, this.testNode.get(KEY_NAME, "<missing preference>"));
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ }
+
+ public void testSetValue() throws Exception {
+ String ANOTHER_STRING_VALUE = "some other value";
+ this.preferenceAdapter.setValue(ANOTHER_STRING_VALUE);
+ assertEquals(ANOTHER_STRING_VALUE, this.preferenceAdapter.getValue());
+ assertEquals(ANOTHER_STRING_VALUE, this.testNode.get(KEY_NAME, "<missing preference>"));
+ }
+
+ public void testHasListeners() throws Exception {
+ assertTrue(this.preferenceAdapter.hasAnyPropertyChangeListeners(ValueModel.VALUE));
+ assertTrue(this.nodeHasAnyPrefListeners(this.testNode));
+ this.preferenceAdapter.removePropertyChangeListener(ValueModel.VALUE, this.listener);
+ assertFalse(this.nodeHasAnyPrefListeners(this.testNode));
+ assertFalse(this.preferenceAdapter.hasAnyPropertyChangeListeners(ValueModel.VALUE));
+
+ PropertyChangeListener listener2 = this.buildValueChangeListener();
+ this.preferenceAdapter.addPropertyChangeListener(listener2);
+ assertTrue(this.preferenceAdapter.hasAnyPropertyChangeListeners(ValueModel.VALUE));
+ assertTrue(this.nodeHasAnyPrefListeners(this.testNode));
+ this.preferenceAdapter.removePropertyChangeListener(listener2);
+ assertFalse(this.nodeHasAnyPrefListeners(this.testNode));
+ assertFalse(this.preferenceAdapter.hasAnyPropertyChangeListeners(ValueModel.VALUE));
+ }
+
+ public void testRemoveAndReAddPreference() throws Exception {
+ assertEquals(STRING_VALUE, this.testNode.get(KEY_NAME, null));
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ assertNull(this.event);
+
+ // remove the preference entirely...
+ this.testNode.remove(KEY_NAME);
+ this.waitForEventQueueToClear();
+ assertNull(this.testNode.get(KEY_NAME, null));
+ this.verifyEvent(STRING_VALUE, null);
+ assertNull(this.preferenceAdapter.getValue());
+
+ // ...then re-add it with the same key
+ this.event = null;
+ this.testNode.put(KEY_NAME, STRING_VALUE);
+ this.waitForEventQueueToClear();
+ assertEquals(STRING_VALUE, this.testNode.get(KEY_NAME, null));
+ this.verifyEvent(null, STRING_VALUE);
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ }
+
+ public void testDefaultValue() throws Exception {
+ // rebuild the adapter with a default value
+ String DEFAULT_VALUE = "default value";
+ this.preferenceAdapter.removePropertyChangeListener(ValueModel.VALUE, this.listener);
+ this.preferenceAdapter = new PreferencePropertyValueModel(this.nodeHolder, KEY_NAME, DEFAULT_VALUE);
+ this.preferenceAdapter.addPropertyChangeListener(ValueModel.VALUE, this.listener);
+
+ assertEquals(STRING_VALUE, this.testNode.get(KEY_NAME, null));
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ assertNull(this.event);
+
+ // remove the preference entirely...
+ this.testNode.remove(KEY_NAME);
+ this.waitForEventQueueToClear();
+ assertNull(this.testNode.get(KEY_NAME, null));
+ this.verifyEvent(STRING_VALUE, DEFAULT_VALUE);
+ assertEquals(DEFAULT_VALUE, this.preferenceAdapter.getValue());
+
+ // ...then re-add it with the same key
+ this.event = null;
+ this.testNode.put(KEY_NAME, STRING_VALUE);
+ this.waitForEventQueueToClear();
+ assertEquals(STRING_VALUE, this.testNode.get(KEY_NAME, null));
+ this.verifyEvent(DEFAULT_VALUE, STRING_VALUE);
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ }
+
+ public void testUnsynchronizedValue() throws Exception {
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ assertNull(this.event);
+
+ // remove the this.listener so the adapter no longer listens to the preference
+ this.preferenceAdapter.removePropertyChangeListener(ValueModel.VALUE, this.listener);
+
+ this.testNode.put(KEY_NAME, STRING_VALUE + STRING_VALUE);
+ this.waitForEventQueueToClear();
+ // no this.event should have been fired...
+ assertNull(this.event);
+ // ...and the adapter's value should be null
+ assertNull(this.preferenceAdapter.getValue());
+
+ this.testNode.remove(KEY_NAME);
+ this.waitForEventQueueToClear();
+ assertNull(this.event);
+ assertNull(this.preferenceAdapter.getValue());
+
+ this.testNode.put(KEY_NAME, STRING_VALUE);
+ this.waitForEventQueueToClear();
+ assertNull(this.event);
+ assertNull(this.preferenceAdapter.getValue());
+
+ // add the this.listener so the adapter synchs
+ this.preferenceAdapter.addPropertyChangeListener(ValueModel.VALUE, this.listener);
+ assertEquals(STRING_VALUE, this.preferenceAdapter.getValue());
+ }
+
+ public void testIntegerPreference() throws Exception {
+ // stop listening to the node and convert it to an integer
+ this.preferenceAdapter.removePropertyChangeListener(ValueModel.VALUE, this.listener);
+ this.testNode.putInt(KEY_NAME, 123);
+ this.preferenceAdapter.setConverter(BidiStringConverter.IntegerConverter.instance());
+ this.preferenceAdapter.addPropertyChangeListener(ValueModel.VALUE, this.listener);
+ assertEquals(new Integer(123), this.preferenceAdapter.getValue());
+ assertNull(this.event);
+
+ this.testNode.putInt(KEY_NAME, 246);
+ this.waitForEventQueueToClear();
+ this.verifyEvent(new Integer(123), new Integer(246));
+ assertEquals(new Integer(246), this.preferenceAdapter.getValue());
+
+ this.event = null;
+ this.testNode.remove(KEY_NAME);
+ this.waitForEventQueueToClear();
+ this.verifyEvent(new Integer(246), null);
+ assertNull(this.preferenceAdapter.getValue());
+
+ this.event = null;
+ this.testNode.putInt(KEY_NAME, 123);
+ this.waitForEventQueueToClear();
+ this.verifyEvent(null, new Integer(123));
+ assertEquals(new Integer(123), this.preferenceAdapter.getValue());
+ }
+
+ /**
+ * test a situation where
+ * - we are listening to the node when it gets removed from the preferences "repository"
+ * - we get notification that it has been removed
+ * - we try to remove our this.listener
+ * - the node will throw an IllegalStateException - the adapter should handle it OK...
+ */
+ public void testRemoveNode() throws Exception {
+ assertTrue(this.preferenceAdapter.hasAnyPropertyChangeListeners(ValueModel.VALUE));
+
+ Preferences parent = this.testNode.parent();
+ parent.addNodeChangeListener(this.buildParentNodeChangeListener());
+ this.testNode.removeNode();
+ this.testNode.flush(); // this seems to be required for the this.event to trigger...
+ this.waitForEventQueueToClear();
+
+ assertTrue(this.listenerRemoved);
+ assertTrue(this.preferenceAdapter.hasNoPropertyChangeListeners(ValueModel.VALUE));
+ }
+
+ private NodeChangeListener buildParentNodeChangeListener() {
+ return new NodeChangeListener() {
+ public void childAdded(NodeChangeEvent e) {
+ throw new IllegalStateException("unexpected this.event: " + e);
+ }
+ public void childRemoved(NodeChangeEvent e) {
+ if (e.getChild() == PreferencePropertyValueModelTests.this.testNode) {
+ PreferencePropertyValueModelTests.this.preferenceAdapter.removePropertyChangeListener(ValueModel.VALUE, PreferencePropertyValueModelTests.this.listener);
+ // this line of code will not execute if the line above triggers an exception
+ PreferencePropertyValueModelTests.this.listenerRemoved = true;
+ }
+ }
+ };
+ }
+
+ public void testSetSameValue() {
+ assertNull(this.event);
+ assertNull(this.preferenceEvent);
+ this.testNode.addPreferenceChangeListener(this.buildPreferenceChangeListener());
+
+ String ANOTHER_STRING_VALUE = "some other value";
+ this.preferenceAdapter.setValue(ANOTHER_STRING_VALUE);
+
+ this.verifyEvent(STRING_VALUE, ANOTHER_STRING_VALUE);
+ this.waitForEventQueueToClear();
+ this.verifyPreferenceEvent(ANOTHER_STRING_VALUE);
+
+ // now set to *same* value - nothing should happen...
+ this.event = null;
+ this.preferenceEvent = null;
+ this.preferenceAdapter.setValue(ANOTHER_STRING_VALUE);
+
+ assertNull(this.event);
+ assertNull(this.preferenceEvent);
+ }
+
+ public void testSetSameValueForcePassThrough() throws Exception {
+ assertNull(this.event);
+ assertNull(this.preferenceEvent);
+
+ this.preferenceAdapter.removePropertyChangeListener(ValueModel.VALUE, this.listener);
+ this.preferenceAdapter = new AlwaysUpdatePreferencePropertyValueModel(this.nodeHolder, KEY_NAME);
+ this.preferenceAdapter.addPropertyChangeListener(ValueModel.VALUE, this.listener);
+
+ this.testNode.addPreferenceChangeListener(this.buildPreferenceChangeListener());
+
+ String ANOTHER_STRING_VALUE = "some other value";
+ this.preferenceAdapter.setValue(ANOTHER_STRING_VALUE);
+
+ this.verifyEvent(STRING_VALUE, ANOTHER_STRING_VALUE);
+ this.waitForEventQueueToClear();
+ this.verifyPreferenceEvent(ANOTHER_STRING_VALUE);
+
+ // now set to *same* value - only one this.event should fire
+ this.event = null;
+ this.preferenceEvent = null;
+ this.preferenceAdapter.setValue(ANOTHER_STRING_VALUE);
+
+ assertNull(this.event);
+ this.waitForEventQueueToClear();
+ this.verifyPreferenceEvent(ANOTHER_STRING_VALUE);
+ assertNull(this.event);
+ }
+
+ private PreferenceChangeListener buildPreferenceChangeListener() {
+ return new PreferenceChangeListener() {
+ public void preferenceChange(PreferenceChangeEvent evt) {
+ PreferencePropertyValueModelTests.this.preferenceEvent = evt;
+ }
+ };
+ }
+
+ private void verifyEvent(Object oldValue, Object newValue) {
+ assertNotNull(this.event);
+ assertEquals(this.preferenceAdapter, this.event.getSource());
+ assertEquals(ValueModel.VALUE, this.event.propertyName());
+ assertEquals(oldValue, this.event.oldValue());
+ assertEquals(newValue, this.event.newValue());
+ }
+
+ private void verifyPreferenceEvent(Object newValue) {
+ assertNotNull(this.preferenceEvent);
+ assertEquals(this.testNode, this.preferenceEvent.getSource());
+ assertEquals(KEY_NAME, this.preferenceEvent.getKey());
+ assertEquals(newValue, this.preferenceEvent.getNewValue());
+ assertEquals(newValue, this.testNode.get(KEY_NAME, "<missing preference>"));
+ }
+
+ private boolean nodeHasAnyPrefListeners(Preferences node) throws Exception {
+ PreferenceChangeListener[] prefListeners = (PreferenceChangeListener[]) ClassTools.getFieldValue(node, "prefListeners");
+ return prefListeners.length > 0;
+ }
+
+
+ /**
+ * Use this adapter to test out always passing through the new value
+ * to the preference.
+ */
+ private class AlwaysUpdatePreferencePropertyValueModel extends PreferencePropertyValueModel {
+
+ AlwaysUpdatePreferencePropertyValueModel(ValueModel preferencesHolder, String key) {
+ super(preferencesHolder, key);
+ }
+
+ @Override
+ protected boolean shouldSetPreference(Object oldValue, Object newValue) {
+ return true;
+ }
+
+ }
+
+}
diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesCollectionValueModelTests.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesCollectionValueModelTests.java
new file mode 100644
index 0000000000..4d4d9f29c4
--- /dev/null
+++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesCollectionValueModelTests.java
@@ -0,0 +1,272 @@
+/*******************************************************************************
+ * 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.tests.internal.model.value.prefs;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.prefs.NodeChangeEvent;
+import java.util.prefs.NodeChangeListener;
+import java.util.prefs.PreferenceChangeListener;
+import java.util.prefs.Preferences;
+
+import org.eclipse.jpt.utility.internal.ClassTools;
+import org.eclipse.jpt.utility.internal.model.event.CollectionChangeEvent;
+import org.eclipse.jpt.utility.internal.model.event.PropertyChangeEvent;
+import org.eclipse.jpt.utility.internal.model.listener.CollectionChangeListener;
+import org.eclipse.jpt.utility.internal.model.listener.PropertyChangeListener;
+import org.eclipse.jpt.utility.internal.model.value.PropertyValueModel;
+import org.eclipse.jpt.utility.internal.model.value.SimplePropertyValueModel;
+import org.eclipse.jpt.utility.internal.model.value.ValueModel;
+import org.eclipse.jpt.utility.internal.model.value.prefs.PreferencePropertyValueModel;
+import org.eclipse.jpt.utility.internal.model.value.prefs.PreferencesCollectionValueModel;
+
+public class PreferencesCollectionValueModelTests extends PreferencesTestCase {
+ private Map expectedValues;
+ private PropertyValueModel nodeHolder;
+ PreferencesCollectionValueModel preferencesAdapter;
+ CollectionChangeEvent event;
+ CollectionChangeListener listener;
+ private PropertyChangeListener itemListener;
+ boolean listenerRemoved = false;
+ private static final String KEY_NAME_1 = "foo 1";
+ private static final String KEY_NAME_2 = "foo 2";
+ private static final String KEY_NAME_3 = "foo 3";
+ private static final String STRING_VALUE_1 = "original string value 1";
+ private static final String STRING_VALUE_2 = "original string value 2";
+ private static final String STRING_VALUE_3 = "original string value 3";
+
+ public PreferencesCollectionValueModelTests(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ this.expectedValues = new HashMap();
+ this.testNode.put(KEY_NAME_1, STRING_VALUE_1); this.expectedValues.put(KEY_NAME_1, STRING_VALUE_1);
+ this.testNode.put(KEY_NAME_2, STRING_VALUE_2); this.expectedValues.put(KEY_NAME_2, STRING_VALUE_2);
+ this.testNode.put(KEY_NAME_3, STRING_VALUE_3); this.expectedValues.put(KEY_NAME_3, STRING_VALUE_3);
+
+ this.nodeHolder = new SimplePropertyValueModel(this.testNode);
+ this.preferencesAdapter = new PreferencesCollectionValueModel(this.nodeHolder);
+ this.listener = this.buildCollectionChangeListener();
+ this.itemListener = this.buildItemListener();
+ this.preferencesAdapter.addCollectionChangeListener(ValueModel.VALUE, this.listener);
+ this.event = null;
+ }
+
+ private CollectionChangeListener buildCollectionChangeListener() {
+ return new CollectionChangeListener() {
+ public void collectionChanged(CollectionChangeEvent e) {
+ this.logEvent(e);
+ }
+ public void collectionCleared(CollectionChangeEvent e) {
+ this.logEvent(e);
+ }
+ public void itemsAdded(CollectionChangeEvent e) {
+ this.logEvent(e);
+ }
+ public void itemsRemoved(CollectionChangeEvent e) {
+ this.logEvent(e);
+ }
+ private void logEvent(CollectionChangeEvent e) {
+ if (PreferencesCollectionValueModelTests.this.event != null) {
+ throw new IllegalStateException("unexpected this.event: " + e);
+ }
+ PreferencesCollectionValueModelTests.this.event = e;
+ }
+ };
+ }
+
+ private PropertyChangeListener buildItemListener() {
+ return new PropertyChangeListener() {
+ public void propertyChanged(PropertyChangeEvent e) {
+ throw new IllegalStateException("unexpected this.event: " + e);
+ }
+ };
+ }
+
+ public void testSubjectHolder() throws Exception {
+ this.verifyAdapter(this.preferencesAdapter);
+ assertNull(this.event);
+
+ String ANOTHER_KEY_NAME_1 = "another key 1";
+ String ANOTHER_KEY_NAME_2 = "another key 2";
+ String ANOTHER_KEY_NAME_3 = "another key 3";
+ String ANOTHER_STRING_VALUE_1 = "another string value 1";
+ String ANOTHER_STRING_VALUE_2 = "another string value 2";
+ String ANOTHER_STRING_VALUE_3 = "another string value 3";
+ Preferences anotherNode = this.classNode.node("another test node");
+ this.expectedValues.clear();
+ anotherNode.put(ANOTHER_KEY_NAME_1, ANOTHER_STRING_VALUE_1); this.expectedValues.put(ANOTHER_KEY_NAME_1, ANOTHER_STRING_VALUE_1);
+ anotherNode.put(ANOTHER_KEY_NAME_2, ANOTHER_STRING_VALUE_2); this.expectedValues.put(ANOTHER_KEY_NAME_2, ANOTHER_STRING_VALUE_2);
+ anotherNode.put(ANOTHER_KEY_NAME_3, ANOTHER_STRING_VALUE_3); this.expectedValues.put(ANOTHER_KEY_NAME_3, ANOTHER_STRING_VALUE_3);
+
+ this.nodeHolder.setValue(anotherNode);
+ // collectionChanged does not pass any items in the this.event
+ this.verifyEvent(Collections.EMPTY_MAP);
+ this.verifyAdapter(this.preferencesAdapter);
+
+ this.event = null;
+ this.expectedValues.clear();
+ this.nodeHolder.setValue(null);
+ this.verifyEvent(this.expectedValues);
+ assertFalse(((Iterator) this.preferencesAdapter.getValue()).hasNext());
+
+ this.event = null;
+ this.nodeHolder.setValue(this.testNode);
+ this.verifyEvent(Collections.EMPTY_MAP);
+ this.expectedValues.clear();
+ this.expectedValues.put(KEY_NAME_1, STRING_VALUE_1);
+ this.expectedValues.put(KEY_NAME_2, STRING_VALUE_2);
+ this.expectedValues.put(KEY_NAME_3, STRING_VALUE_3);
+ this.verifyAdapter(this.preferencesAdapter);
+ }
+
+ public void testAddPreference() throws Exception {
+ this.verifyAdapter(this.preferencesAdapter);
+ assertNull(this.event);
+
+ String ANOTHER_KEY_NAME = "another key";
+ String ANOTHER_STRING_VALUE = "another string value";
+ this.testNode.put(ANOTHER_KEY_NAME, ANOTHER_STRING_VALUE);
+ this.waitForEventQueueToClear();
+ Map expectedItems = new HashMap();
+ expectedItems.put(ANOTHER_KEY_NAME, ANOTHER_STRING_VALUE);
+ this.verifyEvent(expectedItems);
+ this.expectedValues.put(ANOTHER_KEY_NAME, ANOTHER_STRING_VALUE);
+ this.verifyAdapter(this.preferencesAdapter);
+ }
+
+ public void testRemovePreference() throws Exception {
+ this.verifyAdapter(this.preferencesAdapter);
+ assertNull(this.event);
+
+ this.testNode.remove(KEY_NAME_2);
+ this.waitForEventQueueToClear();
+
+ assertNotNull(this.event);
+ assertEquals(this.preferencesAdapter, this.event.getSource());
+ assertEquals(ValueModel.VALUE, this.event.collectionName());
+ assertEquals(1, this.event.itemsSize());
+ assertEquals(KEY_NAME_2, ((PreferencePropertyValueModel) this.event.items().next()).getKey());
+
+ this.expectedValues.remove(KEY_NAME_2);
+ this.verifyAdapter(this.preferencesAdapter);
+ }
+
+ public void testChangePreference() throws Exception {
+ this.verifyAdapter(this.preferencesAdapter);
+ assertNull(this.event);
+
+ String DIFFERENT = "something completely different";
+ this.testNode.put(KEY_NAME_2, DIFFERENT);
+ this.waitForEventQueueToClear();
+
+ assertNull(this.event);
+
+ this.expectedValues.put(KEY_NAME_2, DIFFERENT);
+ this.verifyAdapter(this.preferencesAdapter);
+ }
+
+ public void testGetValue() throws Exception {
+ this.verifyNode(this.testNode);
+ this.verifyAdapter(this.preferencesAdapter);
+ }
+
+ /**
+ * test a situation where
+ * - we are listening to the node when it gets removed from the preferences "repository"
+ * - we get notification that it has been removed
+ * - we try to remove our this.listener
+ * - the node will throw an IllegalStateException - the adapter should handle it OK...
+ */
+ public void testRemoveNode() throws Exception {
+ assertTrue(this.preferencesAdapter.hasAnyCollectionChangeListeners(ValueModel.VALUE));
+
+ Preferences parent = this.testNode.parent();
+ parent.addNodeChangeListener(this.buildParentNodeChangeListener());
+ this.testNode.removeNode();
+ this.testNode.flush(); // this seems to be required for the this.event to trigger...
+ this.waitForEventQueueToClear();
+
+ assertTrue(this.listenerRemoved);
+ assertFalse(this.preferencesAdapter.hasAnyCollectionChangeListeners(ValueModel.VALUE));
+ }
+
+ private NodeChangeListener buildParentNodeChangeListener() {
+ return new NodeChangeListener() {
+ public void childAdded(NodeChangeEvent e) {
+ throw new IllegalStateException("unexpected this.event: " + e);
+ }
+ public void childRemoved(NodeChangeEvent e) {
+ if (e.getChild() == PreferencesCollectionValueModelTests.this.testNode) {
+ PreferencesCollectionValueModelTests.this.preferencesAdapter.removeCollectionChangeListener(ValueModel.VALUE, PreferencesCollectionValueModelTests.this.listener);
+ // this line of code will not execute if the line above triggers an exception
+ PreferencesCollectionValueModelTests.this.listenerRemoved = true;
+ }
+ }
+ };
+ }
+
+ public void testHasListeners() throws Exception {
+ assertTrue(this.preferencesAdapter.hasAnyCollectionChangeListeners(ValueModel.VALUE));
+ assertTrue(this.nodeHasAnyPrefListeners(this.testNode));
+ this.preferencesAdapter.removeCollectionChangeListener(ValueModel.VALUE, this.listener);
+ assertFalse(this.nodeHasAnyPrefListeners(this.testNode));
+ assertFalse(this.preferencesAdapter.hasAnyCollectionChangeListeners(ValueModel.VALUE));
+
+ CollectionChangeListener listener2 = this.buildCollectionChangeListener();
+ this.preferencesAdapter.addCollectionChangeListener(listener2);
+ assertTrue(this.preferencesAdapter.hasAnyCollectionChangeListeners(ValueModel.VALUE));
+ assertTrue(this.nodeHasAnyPrefListeners(this.testNode));
+ this.preferencesAdapter.removeCollectionChangeListener(listener2);
+ assertFalse(this.nodeHasAnyPrefListeners(this.testNode));
+ assertFalse(this.preferencesAdapter.hasAnyCollectionChangeListeners(ValueModel.VALUE));
+ }
+
+ private void verifyEvent(Map items) {
+ assertNotNull(this.event);
+ assertEquals(this.preferencesAdapter, this.event.getSource());
+ assertEquals(ValueModel.VALUE, this.event.collectionName());
+ assertEquals(items.size(), this.event.itemsSize());
+ this.verifyItems(items, this.event.items());
+ }
+
+ private void verifyNode(Preferences node) throws Exception {
+ String[] keys = node.keys();
+ assertEquals(this.expectedValues.size(), keys.length);
+ for (int i = 0; i < keys.length; i++) {
+ assertEquals(this.expectedValues.get(keys[i]), node.get(keys[i], "<missing preference>"));
+ }
+ }
+
+ private void verifyAdapter(PreferencesCollectionValueModel cvm) {
+ assertEquals(this.expectedValues.size(), cvm.size());
+ this.verifyItems(this.expectedValues, (Iterator) cvm.getValue());
+ }
+
+ private void verifyItems(Map expected, Iterator stream) {
+ while (stream.hasNext()) {
+ PreferencePropertyValueModel model = (PreferencePropertyValueModel) stream.next();
+ model.addPropertyChangeListener(ValueModel.VALUE, this.itemListener);
+ assertEquals(expected.get(model.getKey()), model.getValue());
+ model.removePropertyChangeListener(ValueModel.VALUE, this.itemListener);
+ }
+ }
+
+ private boolean nodeHasAnyPrefListeners(Preferences node) throws Exception {
+ PreferenceChangeListener[] prefListeners = (PreferenceChangeListener[]) ClassTools.getFieldValue(node, "prefListeners");
+ return prefListeners.length > 0;
+ }
+
+}
diff --git a/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesTestCase.java b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesTestCase.java
new file mode 100644
index 0000000000..d2186909db
--- /dev/null
+++ b/jpa/tests/org.eclipse.jpt.utility.tests/src/org/eclipse/jpt/utility/tests/internal/model/value/prefs/PreferencesTestCase.java
@@ -0,0 +1,85 @@
+/*******************************************************************************
+ * 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.tests.internal.model.value.prefs;
+
+import java.util.List;
+import java.util.prefs.AbstractPreferences;
+import java.util.prefs.Preferences;
+
+import org.eclipse.jpt.utility.internal.ClassTools;
+import org.eclipse.jpt.utility.tests.internal.TestTools;
+
+import junit.framework.TestCase;
+
+/**
+ * set up and tear down a test node for any subclass that
+ * needs to test preferences-related stuff
+ */
+public abstract class PreferencesTestCase extends TestCase {
+ protected Preferences classNode;
+ public Preferences testNode;
+ protected static final String TEST_NODE_NAME = "test node";
+
+ public PreferencesTestCase(String name) {
+ super(name);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Preferences packageNode = Preferences.userNodeForPackage(this.getClass());
+ this.classNode = packageNode.node(ClassTools.shortClassNameForObject(this));
+ // clean out any leftover crap...
+ if ((this.classNode.keys().length > 0) || (this.classNode.childrenNames().length > 0)) {
+ this.classNode.removeNode();
+ // ...and re-create the node
+ this.classNode = packageNode.node(ClassTools.shortClassNameForObject(this));
+ }
+ this.testNode = this.classNode.node(TEST_NODE_NAME);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // wait for all the events to be delivered before tearing down
+ this.waitForEventQueueToClear();
+ Preferences node = this.classNode.parent();
+ this.classNode.removeNode();
+ while (this.nodeIsVestigial(node)) {
+ Preferences parent = node.parent();
+ node.removeNode();
+ node = parent;
+ }
+ TestTools.clear(this);
+ super.tearDown();
+ }
+
+ private boolean nodeIsVestigial(Preferences node) throws Exception {
+ return (node != null)
+ && (node.keys().length == 0)
+ && (node.childrenNames().length == 0)
+ && (node != Preferences.userRoot());
+ }
+
+ protected void waitForEventQueueToClear() {
+ try {
+ while ( ! this.preferencesEventQueue().isEmpty()) {
+ Thread.sleep(100);
+ }
+ Thread.sleep(100);
+ } catch (InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private List preferencesEventQueue() {
+ return (List) ClassTools.getStaticFieldValue(AbstractPreferences.class, "eventQueue");
+ }
+
+}

Back to the top