diff options
Diffstat (limited to 'jpa')
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"); + } + +} |