diff options
Diffstat (limited to 'common')
38 files changed, 4263 insertions, 1412 deletions
diff --git a/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/properties/JptProjectPropertiesPage.java b/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/properties/JptProjectPropertiesPage.java index 373833d6a6..df59dc5316 100644 --- a/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/properties/JptProjectPropertiesPage.java +++ b/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/properties/JptProjectPropertiesPage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2011, 2013 Oracle. All rights reserved. + * Copyright (c) 2011, 2016 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. @@ -73,7 +73,7 @@ public abstract class JptProjectPropertiesPage public JptProjectPropertiesPage() { super(); - this.projectModel = new SimplePropertyValueModel<IProject>(); + this.projectModel = new SimplePropertyValueModel<>(); this.trigger = new BufferedModifiablePropertyValueModel.Trigger(); this.buildModels(); @@ -438,7 +438,7 @@ public abstract class JptProjectPropertiesPage @Override protected IStatus performValidation() { - HashMap<Integer, ArrayList<IStatus>> statuses = new HashMap<Integer, ArrayList<IStatus>>(); + HashMap<Integer, ArrayList<IStatus>> statuses = new HashMap<>(); statuses.put(ERROR_STATUS, new ArrayList<IStatus>()); statuses.put(WARNING_STATUS, new ArrayList<IStatus>()); statuses.put(INFO_STATUS, new ArrayList<IStatus>()); diff --git a/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/AddRemovePane.java b/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/AddRemovePane.java index c743b4d086..cd88a5a9f9 100644 --- a/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/AddRemovePane.java +++ b/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/AddRemovePane.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2013 Oracle. All rights reserved. + * Copyright (c) 2008, 2016 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. @@ -16,7 +16,7 @@ import org.eclipse.jpt.common.utility.internal.iterable.EmptyIterable; import org.eclipse.jpt.common.utility.internal.iterable.EmptyListIterable; import org.eclipse.jpt.common.utility.internal.iterable.IterableTools; import org.eclipse.jpt.common.utility.internal.iterable.SingleElementIterable; -import org.eclipse.jpt.common.utility.internal.model.value.CollectionPropertyValueModelAdapter; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; import org.eclipse.jpt.common.utility.model.Model; import org.eclipse.jpt.common.utility.model.event.ListAddEvent; import org.eclipse.jpt.common.utility.model.event.ListChangeEvent; @@ -753,25 +753,15 @@ public abstract class AddRemovePane<T extends Model, E extends Object> extends P } protected PropertyValueModel<Boolean> buildSingleSelectedItemEnabledModel(CollectionValueModel<E> selectedItemsModel) { - return new CollectionPropertyValueModelAdapter<Boolean, Object>(selectedItemsModel) { - @Override - protected Boolean buildValue() { - return Boolean.valueOf(this.collectionModel.size() == 1); - } - }; + return CollectionValueModelTools.containsSingleElementPropertyValueModel(selectedItemsModel); } public PropertyValueModel<Boolean> buildRemoveButtonEnabledModel(CollectionValueModel<E> selectedItemsModel) { - return this.buildMultipleSelectedItemsEnabledModel(selectedItemsModel); + return this.buildOneOrMoreSelectedItemsEnabledModel(selectedItemsModel); } - protected PropertyValueModel<Boolean> buildMultipleSelectedItemsEnabledModel(CollectionValueModel<E> selectedItemsModel) { - return new CollectionPropertyValueModelAdapter<Boolean, E>(selectedItemsModel) { - @Override - protected Boolean buildValue() { - return Boolean.valueOf(this.collectionModel.size() >= 1); - } - }; + protected PropertyValueModel<Boolean> buildOneOrMoreSelectedItemsEnabledModel(CollectionValueModel<E> selectedItemsModel) { + return CollectionValueModelTools.isNotEmptyPropertyValueModel(selectedItemsModel); } /* diff --git a/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/Pane.java b/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/Pane.java index 93616257cc..f6d95bbaed 100644 --- a/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/Pane.java +++ b/common/plugins/org.eclipse.jpt.common.ui/src/org/eclipse/jpt/common/ui/internal/widgets/Pane.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2013 Oracle. All rights reserved. + * Copyright (c) 2008, 2016 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. @@ -27,7 +27,7 @@ import org.eclipse.jpt.common.ui.internal.swt.TriStateCheckBoxModelAdapter; import org.eclipse.jpt.common.ui.internal.swt.bindings.SWTBindingTools; import org.eclipse.jpt.common.ui.internal.swt.events.DisposeAdapter; import org.eclipse.jpt.common.ui.internal.swt.listeners.SWTListenerTools; -import org.eclipse.jpt.common.utility.internal.model.value.CompositeBooleanPropertyValueModel; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; import org.eclipse.jpt.common.utility.internal.model.value.NullCheckPropertyValueModelWrapper; import org.eclipse.jpt.common.utility.internal.model.value.PredicatePropertyValueModel; import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; @@ -334,11 +334,10 @@ public abstract class Pane<T extends Model> { * pane is <em>enabled</em> <em>and</em> the pane's model indicates the * pane should be <em>enabled</em>. */ - @SuppressWarnings("unchecked") private static PropertyValueModel<Boolean> andEnabledModel(Pane<?> pane, PropertyValueModel<Boolean> enabledModel) { enabledModel = buildNonNullModel(enabledModel); // NB: we fetch private state from the pane - return (pane == null) ? enabledModel : CompositeBooleanPropertyValueModel.and(pane.enabledModel, enabledModel); + return (pane == null) ? enabledModel : CollectionValueModelTools.and(pane.enabledModel, enabledModel); } /** @@ -351,7 +350,7 @@ public abstract class Pane<T extends Model> { * (which is typical with aspect adapters etc.). */ private static PropertyValueModel<Boolean> buildNonNullModel(PropertyValueModel<Boolean> booleanModel) { - return new NullCheckPropertyValueModelWrapper<Boolean>(booleanModel, Boolean.FALSE); + return new NullCheckPropertyValueModelWrapper<>(booleanModel, Boolean.FALSE); } diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPluggablePropertyValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPluggablePropertyValueModel.java new file mode 100644 index 0000000000..7dcf3cc7c9 --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPluggablePropertyValueModel.java @@ -0,0 +1,239 @@ +/******************************************************************************* + * Copyright (c) 2008, 2016 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.common.utility.internal.model.value; + +import org.eclipse.jpt.common.utility.internal.ObjectTools; +import org.eclipse.jpt.common.utility.internal.model.AbstractModel; +import org.eclipse.jpt.common.utility.internal.model.ChangeSupport; +import org.eclipse.jpt.common.utility.internal.model.SingleAspectChangeSupport; +import org.eclipse.jpt.common.utility.model.listener.ChangeListener; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; + +/** + * This class provides the infrastructure needed to wrap + * a model, "lazily" listen to it, and convert + * its change notifications into <em>property</em> value model change + * notifications. + * + * @param <V> the type of the model's derived value + */ +public abstract class AbstractPluggablePropertyValueModel<V, A extends AbstractPluggablePropertyValueModel.Adapter<V>> + extends AbstractModel + implements PropertyValueModel<V> +{ + /** + * Adapter that listens to some model and + * calls back whenever that model changes in a way that + * affects this model's value. + */ + protected final A adapter; + + /** + * Cache the current value so we can pass an "old value" when + * we fire a property change event. + * We need this because the value may be calculated and we may + * not able to derive the "old value" from any fired events. + */ + protected volatile V value; + + + // ********** constructor/initialization ********** + + protected AbstractPluggablePropertyValueModel(Adapter.Factory<V, A> adapterFactory) { + super(); + if (adapterFactory == null) { + throw new NullPointerException(); + } + // a bit of instance leakage... + this.adapter = adapterFactory.buildAdapter(new AdapterListener()); + // our value is null when we are not listening to the model + this.value = null; + } + + @Override + protected ChangeSupport buildChangeSupport() { + return new SingleAspectChangeSupport(this, PropertyChangeListener.class, PropertyValueModel.VALUE); + } + + + // ********** PropertyValueModel implementation ********** + + /** + * Return the cached value. + */ + public V getValue() { + return this.value; + } + + + // ********** listeners ********** + + /** + * Extend to start listening to the underlying model if necessary. + */ + @Override + public synchronized void addChangeListener(ChangeListener listener) { + if (this.hasNoListeners()) { + this.engageModel(); + } + super.addChangeListener(listener); + } + + /** + * Extend to stop listening to the underlying model if necessary. + */ + @Override + public synchronized void removeChangeListener(ChangeListener listener) { + super.removeChangeListener(listener); + if (this.hasNoListeners()) { + this.disengageModel(); + } + } + + /** + * Extend to start listening to the underlying model if necessary. + */ + @Override + public synchronized void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) { + if (propertyName.equals(PropertyValueModel.VALUE) && this.hasNoListeners()) { + this.engageModel(); + } + super.addPropertyChangeListener(propertyName, listener); + } + + /** + * Extend to stop listening to the underlying model if necessary. + */ + @Override + public synchronized void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) { + super.removePropertyChangeListener(propertyName, listener); + if (propertyName.equals(PropertyValueModel.VALUE) && this.hasNoListeners()) { + this.disengageModel(); + } + } + + /** + * Return whether the model has no property value listeners. + */ + public boolean hasNoListeners() { + return ! this.hasListeners(); + } + + /** + * Return whether the model has any property value listeners. + */ + public boolean hasListeners() { + return this.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE); + } + + /** + * Start listening to the underlying model and build the value. + */ + protected void engageModel() { + this.adapter.engageModel(); + // sync our value *after* we start listening to the model, + // since the model's value might change when a listener is added + this.value = this.adapter.getValue(); + } + + /** + * Clear the value and stop listening to the underlying model. + */ + private void disengageModel() { + // clear out our value when we are not listening to the model + this.value = null; + this.adapter.disengageModel(); + } + + + // ********** Misc ********** + + /** + * The underlying model changed in some fashion. + * Notify our listeners. + */ + /* CU private */ void valueChanged(V newValue) { + Object old = this.value; + this.firePropertyChanged(VALUE, old, this.value = newValue); + } + + @Override + public void toString(StringBuilder sb) { + sb.append(this.value); + } + + + // ********** Adapter Listener ********** + + /** + * Simple callback. + */ + /* CU private */ class AdapterListener + implements Adapter.Listener<V> + { + public void valueChanged(V newValue) { + AbstractPluggablePropertyValueModel.this.valueChanged(newValue); + } + @Override + public String toString() { + return ObjectTools.toString(this); + } + } + + + // ********** Adapter interfaces ********** + + /** + * Minimal methods necessary to adapt some model to {@link PropertyValueModel}. + * This adapter is expected to listen to its adapted model and notify the + * listener passed to it via its {@link Adapter.Factory factory} + * about any model changes. + */ + public interface Adapter<AV> { + /** + * Return the current property value, as derived from the + * current state of the adapted model. + */ + AV getValue(); + + /** + * Start listening to the adapted model. + */ + void engageModel(); + + /** + * Stop listening to the adapted model. + */ + void disengageModel(); + + /** + * Callback interface. + */ + interface Listener<ALV> { + /** + * Callback to notify listener that the adapted model has changed. + */ + void valueChanged(ALV newValue); + } + + /** + * Adapter factory interface. + * This factory allows both the pluggable property value model and its + * adapter to have circular <em>final</em> references to each other. + */ + interface Factory<AFV, A extends Adapter<AFV>> { + /** + * Create an adapter with the specified listener. + */ + A buildAdapter(Listener<AFV> listener); + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPropertyValueModelAdapter.java deleted file mode 100644 index a77b11b6cf..0000000000 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPropertyValueModelAdapter.java +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2012 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.common.utility.internal.model.value; - -import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; - -/** - * This abstract class provides the infrastructure needed to wrap - * a model, "lazily" listen to it, and convert - * its change notifications into <em>property</em> value model change - * notifications. - * <p> - * Subclasses must implement:<ul> - * <li>{@link #buildValue()}<p> - * to return the current property value, as derived from the - * current state of the underlying model - * <li>{@link #engageModel_()}<p> - * to start listening to the underlying (adapted) model - * <li>{@link #disengageModel_()}<p> - * to stop listening to the underlying (adapted) model - * </ul> - * Subclasses can call {@link #propertyChanged()} whenever the calculated - * value of the property changes (as determined by the subclass). - * - * @param <V> the type of the model's value - */ -public abstract class AbstractPropertyValueModelAdapter<V> - extends AbstractPropertyValueModel - implements PropertyValueModel<V> -{ - /** - * Cache the current value so we can pass an "old value" when - * we fire a property change event. - * We need this because the value may be calculated and we may - * not able to derive the "old value" from any fired events. - */ - protected volatile V value; - - - // ********** constructor/initialization ********** - - protected AbstractPropertyValueModelAdapter() { - super(); - // our value is null when we are not listening to the model - this.value = null; - } - - - // ********** PropertyValueModel implementation ********** - - /** - * Return the cached value. - */ - public V getValue() { - return this.value; - } - - - // ********** behavior ********** - - /** - * Start listening to the underlying model and build the value. - */ - @Override - protected void engageModel() { - this.engageModel_(); - // sync our value *after* we start listening to the model, - // since the model's value might change when a listener is added - this.value = this.buildValue(); - } - - /** - * Start listening to the underlying model. - */ - protected abstract void engageModel_(); - - /** - * Build and return the current value, as derived from the - * current state of the underlying model. - */ - protected abstract V buildValue(); - - /** - * Stop listening to the underlying model and clear the value. - */ - @Override - protected void disengageModel() { - this.disengageModel_(); - // clear out our value when we are not listening to the model - this.value = null; - } - - /** - * Stop listening to the underlying model. - */ - protected abstract void disengageModel_(); - - /** - * The underlying model changed in some fashion. - * Recalculate the model's value and notify listeners. - */ - protected void propertyChanged() { - Object old = this.value; - this.firePropertyChanged(VALUE, old, this.value = this.buildValue()); - } - - @Override - public void toString(StringBuilder sb) { - sb.append(this.value); - } -} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionPluggablePropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionPluggablePropertyValueModelAdapter.java new file mode 100644 index 0000000000..4f2c0387c0 --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionPluggablePropertyValueModelAdapter.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import org.eclipse.jpt.common.utility.internal.ObjectTools; +import org.eclipse.jpt.common.utility.internal.collection.CollectionTools; +import org.eclipse.jpt.common.utility.internal.model.value.PluggablePropertyValueModel.Adapter; +import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent; +import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; +import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; +import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent; +import org.eclipse.jpt.common.utility.model.listener.CollectionChangeListener; +import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.transformer.Transformer; + +/** + * Adapt a {@link CollectionValueModel collection value model} to + * a {@link PropertyValueModel property value model}, sorta. + * <p> + * This adapter is constructed with a {@link CollectionValueModel + * collection value model} and a {@link Transformer transformer} that can + * transform the collection to a single value. + * <p> + * This is an adapter that can be plugged into a {@link PluggablePropertyValueModel}. + * + * @param <E> the type of the adapted collection value model's elements + * @param <V> the type of the model's derived value + * + * @see PluggablePropertyValueModel + */ +public final class CollectionPluggablePropertyValueModelAdapter<E, V> + implements PluggablePropertyValueModel.Adapter<V>, CollectionChangeListener +{ + private final Factory<E, V> factory; + + /** The <em>real</em> adapter. */ + private final AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener; + + /** Cached copy of model's elements. */ + private final LinkedList<E> collection; + + /** Protects {@link #collection} from {@link Factory#transformer}. */ + private final Collection<E> unmodifiableCollection; + + /** The derived value. */ + private volatile V value; + + + // ********** constructors ********** + + public CollectionPluggablePropertyValueModelAdapter(Factory<E, V> factory, AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + super(); + if (factory == null) { + throw new NullPointerException(); + } + this.factory = factory; + if (listener == null) { + throw new NullPointerException(); + } + this.listener = listener; + this.collection = new LinkedList<>(); + this.unmodifiableCollection = Collections.unmodifiableCollection(this.collection); + } + + + // ********** PropertyValueModelAdapter.Adapter ********** + + public V getValue() { + return this.value; + } + + public void engageModel() { + this.factory.collectionModel.addCollectionChangeListener(CollectionValueModel.VALUES, this); + CollectionTools.addAll(this.collection, this.factory.collectionModel); + this.value = this.buildValue(); + } + + public void disengageModel() { + this.value = null; + this.collection.clear(); + this.factory.collectionModel.removeCollectionChangeListener(CollectionValueModel.VALUES, this); + } + + + // ********** CollectionChangeListener ********** + + @SuppressWarnings("unchecked") + public void itemsAdded(CollectionAddEvent event) { + CollectionTools.addAll(this.collection, (Iterable<E>) event.getItems()); + this.update(); + } + + public void itemsRemoved(CollectionRemoveEvent event) { + CollectionTools.removeAll(this.collection, event.getItems()); + this.update(); + } + + public void collectionCleared(CollectionClearEvent event) { + this.collection.clear(); + this.update(); + } + + @SuppressWarnings("unchecked") + public void collectionChanged(CollectionChangeEvent event) { + this.collection.clear(); + CollectionTools.addAll(this.collection, (Iterable<E>) event.getCollection()); + this.update(); + } + + + // ********** misc ********** + + private void update() { + V newValue = this.buildValue(); + this.value = newValue; + this.listener.valueChanged(newValue); + } + + private V buildValue() { + return this.factory.transformer.transform(this.unmodifiableCollection); + } + + @Override + public String toString() { + return ObjectTools.toString(this, this.value); + } + + + // ********** PluggablePropertyValueModel.Adapter.Factory ********** + + public static class Factory<E, V> + implements PluggablePropertyValueModel.Adapter.Factory<V> + { + /* CU private */ final CollectionValueModel<? extends E> collectionModel; + /* CU private */ final Transformer<? super Collection<E>, V> transformer; + + public Factory(CollectionValueModel<? extends E> collectionModel, Transformer<? super Collection<E>, V> transformer) { + super(); + if (collectionModel == null) { + throw new NullPointerException(); + } + this.collectionModel = collectionModel; + if (transformer == null) { + throw new NullPointerException(); + } + this.transformer = transformer; + } + + public Adapter<V> buildAdapter(AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + return new CollectionPluggablePropertyValueModelAdapter<>(this, listener); + } + + @Override + public String toString() { + return ObjectTools.toString(this); + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionPropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionPropertyValueModelAdapter.java deleted file mode 100644 index 1f636e5fd1..0000000000 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionPropertyValueModelAdapter.java +++ /dev/null @@ -1,174 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2007, 2012 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.common.utility.internal.model.value; - -import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent; -import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; -import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; -import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent; -import org.eclipse.jpt.common.utility.model.listener.CollectionChangeAdapter; -import org.eclipse.jpt.common.utility.model.listener.CollectionChangeListener; -import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; - -/** - * This abstract class provides the infrastructure needed to wrap - * a collection value model, "lazily" listen to it, and convert - * its change notifications into property value model change - * notifications. - * <p> - * Subclasses must override:<ul> - * <li>{@link #buildValue()}<p> - * to return the current property value, as derived from the - * current collection value - * </ul> - * Subclasses might want to override the following methods - * to improve performance (by not recalculating the value, if possible):<ul> - * <li>{@link #itemsAdded(CollectionAddEvent event)} - * <li>{@link #itemsAdded(Iterable)} - * <li>{@link #itemsRemoved(CollectionRemoveEvent event)} - * <li>{@link #itemsRemoved(Iterable)} - * <li>{@link #collectionCleared(CollectionClearEvent event)} - * <li>{@link #collectionChanged(CollectionChangeEvent event)} - * </ul> - * - * @param <V> the type of the model's value - * @param <E> the type of the wrapped collection value model's elements - */ -public abstract class CollectionPropertyValueModelAdapter<V, E> - extends AbstractPropertyValueModelAdapter<V> -{ - /** The wrapped collection value model. */ - protected final CollectionValueModel<? extends E> collectionModel; - - /** A listener that allows us to sync with changes to the wrapped collection model. */ - protected final CollectionChangeListener collectionListener; - - - // ********** constructor/initialization ********** - - /** - * Construct a property value model with the specified wrapped - * collection value model. - */ - protected CollectionPropertyValueModelAdapter(CollectionValueModel<? extends E> collectionModel) { - super(); - if (collectionModel == null) { - throw new NullPointerException(); - } - this.collectionModel = collectionModel; - this.collectionListener = this.buildCollectionListener(); - } - - protected CollectionChangeListener buildCollectionListener() { - return new CollectionListener(); - } - - /** - * Straightforward callbacks to the adapter. - */ - protected class CollectionListener - extends CollectionChangeAdapter - { - @Override - public void itemsAdded(CollectionAddEvent event) { - CollectionPropertyValueModelAdapter.this.itemsAdded(event); - } - @Override - public void itemsRemoved(CollectionRemoveEvent event) { - CollectionPropertyValueModelAdapter.this.itemsRemoved(event); - } - @Override - public void collectionCleared(CollectionClearEvent event) { - CollectionPropertyValueModelAdapter.this.collectionCleared(event); - } - @Override - public void collectionChanged(CollectionChangeEvent event) { - CollectionPropertyValueModelAdapter.this.collectionChanged(event); - } - } - - - // ********** listener ********** - - /** - * Start listening to the collection holder. - */ - @Override - protected void engageModel_() { - this.collectionModel.addCollectionChangeListener(CollectionValueModel.VALUES, this.collectionListener); - } - - /** - * Stop listening to the collection holder. - */ - @Override - protected void disengageModel_() { - this.collectionModel.removeCollectionChangeListener(CollectionValueModel.VALUES, this.collectionListener); - } - - - // ********** collection change support ********** - - /** - * Items were added to the wrapped collection holder; - * propagate the change notification appropriately. - */ - protected void itemsAdded(CollectionAddEvent event) { - @SuppressWarnings("unchecked") - Iterable<E> items = (Iterable<E>) event.getItems(); - this.itemsAdded(items); - } - - /** - * The specified items were added to the wrapped collection holder; - * propagate the change notification appropriately. - */ - protected void itemsAdded(@SuppressWarnings("unused") Iterable<E> items) { - // by default, simply recalculate the value and fire an event - this.propertyChanged(); - } - - /** - * Items were removed from the wrapped collection holder; - * propagate the change notification appropriately. - */ - protected void itemsRemoved(CollectionRemoveEvent event) { - @SuppressWarnings("unchecked") - Iterable<E> items = (Iterable<E>) event.getItems(); - this.itemsRemoved(items); - } - - /** - * The specified items were removed from the wrapped collection holder; - * propagate the change notification appropriately. - */ - protected void itemsRemoved(@SuppressWarnings("unused") Iterable<E> items) { - // by default, simply recalculate the value and fire an event if necessary - this.propertyChanged(); - } - - /** - * The wrapped collection holder was cleared; - * propagate the change notification appropriately. - */ - protected void collectionCleared(@SuppressWarnings("unused") CollectionClearEvent event) { - // by default, simply recalculate the value and fire an event if necessary - this.propertyChanged(); - } - - /** - * The value of the wrapped collection holder has changed; - * propagate the change notification appropriately. - */ - protected void collectionChanged(@SuppressWarnings("unused") CollectionChangeEvent event) { - // by default, simply recalculate the value and fire an event if necessary - this.propertyChanged(); - } -} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelTools.java new file mode 100644 index 0000000000..cf3ab7d6ae --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelTools.java @@ -0,0 +1,342 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import java.util.Arrays; +import java.util.Collection; +import org.eclipse.jpt.common.utility.closure.Closure; +import org.eclipse.jpt.common.utility.internal.closure.BooleanClosure; +import org.eclipse.jpt.common.utility.internal.closure.ClosureTools; +import org.eclipse.jpt.common.utility.internal.transformer.TransformerAdapter; +import org.eclipse.jpt.common.utility.internal.transformer.TransformerTools; +import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.predicate.Predicate; +import org.eclipse.jpt.common.utility.transformer.Transformer; + +/** + * {@link CollectionValueModel Collection value model} utility methods. + */ +public final class CollectionValueModelTools { + + // ********** composite PVMs ********** + + /** + * Construct a composite property value model adapter for the specified + * transformer and property value models. + */ + @SafeVarargs + public static <E, V> PropertyValueModel<V> compositePropertyValueModel(Transformer<? super Collection<E>, V> transformer, PropertyValueModel<? extends E>... propertyValueModels) { + return compositePropertyValueModel(Arrays.asList(propertyValueModels), transformer); + } + + /** + * Construct a composite property value model adapter for the specified + * property value models and transformer. + */ + public static <E, V> PropertyValueModel<V> compositePropertyValueModel(Collection<? extends PropertyValueModel<? extends E>> propertyValueModels, Transformer<? super Collection<E>, V> transformer) { + return compositePropertyValueModel(new StaticCollectionValueModel<>(propertyValueModels), transformer); + } + + /** + * Construct a composite property value model adapter for the specified + * collection value model and transformer. + * @see PluggablePropertyValueModel + */ + public static <E, V> PropertyValueModel<V> compositePropertyValueModel(CollectionValueModel<? extends PropertyValueModel<? extends E>> collectionModel, Transformer<? super Collection<E>, V> transformer) { + return PropertyValueModelTools.propertyValueModel(new CompositePropertyValueModelAdapter.Factory<>(collectionModel, transformer)); + } + + + // ********** collection meta data adapters ********** + + /** + * Construct a property value model adapter for the specified + * collection value model. + * If the collection is empty, the model's value is <code>null</code>; + * otherwise, it is the first element in the collection. + */ + public static <E> PropertyValueModel<E> firstElementPropertyValueModel(CollectionValueModel<? extends E> collectionModel) { + return propertyValueModel(collectionModel, TransformerTools.collectionFirstElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * collection value model. + * If the collection is empty, the model's value is <code>null</code>; + * otherwise, it is the last element in the collection. + */ + public static <E> PropertyValueModel<E> lastElementPropertyValueModel(CollectionValueModel<? extends E> collectionModel) { + return propertyValueModel(collectionModel, TransformerTools.collectionLastElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * collection value model. + * If the collection contains <em>only</em> a single element, + * the model's value is that element; otherwise, + * the model's value is <code>null</code>. + */ + public static <E> PropertyValueModel<E> singleElementPropertyValueModel(CollectionValueModel<? extends E> collectionModel) { + return propertyValueModel(collectionModel, TransformerTools.collectionSingleElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * collection value model that returns whether the collection is <em>not</em> empty. + */ + public static PropertyValueModel<Boolean> isNotEmptyPropertyValueModel(CollectionValueModel<?> collectionModel) { + return propertyValueModel(collectionModel, TransformerTools.collectionIsNotEmptyTransformer()); + } + + /** + * Construct a <em>modifiable</em> property value model adapter for the specified + * collection value model that returns whether the collection is <em>not</em> empty. + * This model can also be used to populate or clear the collection value model. + * The specified collection mutator is used to convert the collection value model: + * it should populate the collection value model when passed <code>true</code> + * and clear the collection value model when passed <code>false</code>. + */ + public static ModifiablePropertyValueModel<Boolean> isNotEmptyModifiablePropertyValueModel(CollectionValueModel<?> collectionModel, BooleanClosure.Adapter collectionMutator) { + return booleanModifiablePropertyValueModel(collectionModel, TransformerTools.collectionIsNotEmptyTransformer(), collectionMutator); + } + + /** + * Construct a property value model adapter for the specified + * collection value model that returns whether the collection is empty. + */ + public static PropertyValueModel<Boolean> isEmptyPropertyValueModel(CollectionValueModel<?> collectionModel) { + return propertyValueModel(collectionModel, TransformerTools.collectionIsEmptyTransformer()); + } + + /** + * Construct a <em>modifiable</em> property value model adapter for the specified + * collection value model that returns whether the collection is empty. + * This model can also be used to populate or clear the collection value model. + * The specified collection mutator is used to convert the collection value model: + * it should clear the collection value model when passed <code>true</code> + * and populate the collection value model when passed <code>false</code>. + */ + public static ModifiablePropertyValueModel<Boolean> isEmptyModifiablePropertyValueModel(CollectionValueModel<?> collectionModel, BooleanClosure.Adapter collectionMutator) { + return booleanModifiablePropertyValueModel(collectionModel, TransformerTools.collectionIsEmptyTransformer(), collectionMutator); + } + + /** + * Construct a property value model adapter for the specified + * collection value model that returns whether the collection contains + * exactly one element. + */ + public static PropertyValueModel<Boolean> containsSingleElementPropertyValueModel(CollectionValueModel<?> collectionModel) { + return propertyValueModel(collectionModel, TransformerTools.collectionContainsSingleElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * collection value model that returns whether the collection's size + * equals the specified size. + */ + public static PropertyValueModel<Boolean> sizeEqualsPropertyValueModel(CollectionValueModel<?> collectionModel, int size) { + return propertyValueModel(collectionModel, TransformerTools.collectionSizeEqualsTransformer(size)); + } + + + // ********** boolean PVM adapters ********** + + /** + * Construct a boolean property value model that is a composite AND of the + * specified boolean property value models; i.e. the model's value is {@link Boolean#TRUE} + * if all the contained models' values are {@link Boolean#TRUE}, + * otherwise its value is {@link Boolean#FALSE}. + * The model's default value, when it contains no nested models, is {@link Boolean#TRUE}. + */ + @SafeVarargs + public static PropertyValueModel<Boolean> and(PropertyValueModel<Boolean>... models) { + return compositePropertyValueModel(AND_TRANSFORMER, models); + } + + /** + * Construct a boolean property value model that is a composite AND of the + * specified boolean property value models; i.e. the model's value is {@link Boolean#TRUE} + * if all the contained models' values are {@link Boolean#TRUE}, + * otherwise its value is {@link Boolean#FALSE}. + * The model's default value, when it contains no nested models, is {@link Boolean#TRUE}. + */ + public static PropertyValueModel<Boolean> and(Collection<? extends PropertyValueModel<Boolean>> models) { + return compositePropertyValueModel(models, AND_TRANSFORMER); + } + + /** + * Construct a boolean property value model that is a composite AND of the + * specified boolean property value models; i.e. the model's value is {@link Boolean#TRUE} + * if all the contained models' values are {@link Boolean#TRUE}, + * otherwise its value is {@link Boolean#FALSE}. + * The model's default value, when it contains no nested models, is {@link Boolean#TRUE}. + */ + public static PropertyValueModel<Boolean> and(CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { + return compositePropertyValueModel(collectionModel, AND_TRANSFORMER); + } + + /** + * @see AndTransformer + */ + public static final Transformer<Collection<Boolean>, Boolean> AND_TRANSFORMER = new AndTransformer(); + + /** + * A transformer that transforms a collection of {@link Boolean}s into a single + * {@link Boolean} by ANDing them together. Its default value is {@link Boolean#TRUE}. + * @see #and(CollectionValueModel) + */ + public static final class AndTransformer + extends TransformerAdapter<Collection<Boolean>, Boolean> + { + @Override + public Boolean transform(Collection<Boolean> booleans) { + for (Boolean b : booleans) { + if ( ! b.booleanValue()) { + return Boolean.FALSE; + } + } + return Boolean.TRUE; + } + } + + /** + * Construct a boolean property value model that is a composite OR of the + * specified boolean property value models; i.e. the model's value is {@link Boolean#FALSE} + * if all the contained models' values are {@link Boolean#FALSE}, + * otherwise its value is {@link Boolean#TRUE}. + * The model's default value, when it contains no nested models, is {@link Boolean#FALSE}. + */ + @SafeVarargs + public static PropertyValueModel<Boolean> or(PropertyValueModel<Boolean>... models) { + return compositePropertyValueModel(OR_TRANSFORMER, models); + } + + /** + * Construct a boolean property value model that is a composite OR of the + * specified boolean property value models; i.e. the model's value is {@link Boolean#FALSE} + * if all the contained models' values are {@link Boolean#FALSE}, + * otherwise its value is {@link Boolean#TRUE}. + * The model's default value, when it contains no nested models, is {@link Boolean#FALSE}. + */ + public static PropertyValueModel<Boolean> or(Collection<? extends PropertyValueModel<Boolean>> models) { + return compositePropertyValueModel(models, OR_TRANSFORMER); + } + + /** + * Construct a boolean property value model that is a composite OR of the + * specified boolean property value models; i.e. the model's value is {@link Boolean#FALSE} + * if all the contained models' values are {@link Boolean#FALSE}, + * otherwise its value is {@link Boolean#TRUE}. + * The model's default value, when it contains no nested models, is {@link Boolean#FALSE}. + */ + public static PropertyValueModel<Boolean> or(CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { + return compositePropertyValueModel(collectionModel, OR_TRANSFORMER); + } + + /** + * @see OrTransformer + */ + public static final Transformer<Collection<Boolean>, Boolean> OR_TRANSFORMER = new OrTransformer(); + + /** + * A transformer that transforms a collection of {@link Boolean}s into a single + * {@link Boolean} by ORing them together. Its default value is {@link Boolean#FALSE}. + * @see #or(CollectionValueModel) + */ + public static final class OrTransformer + extends TransformerAdapter<Collection<Boolean>, Boolean> + { + @Override + public Boolean transform(Collection<Boolean> booleans) { + for (Boolean b : booleans) { + if (b.booleanValue()) { + return Boolean.TRUE; + } + } + return Boolean.FALSE; + } + } + + + // ********** PVM adapters ********** + + /** + * Construct a boolean property value model adapter for the specified + * collection value model and predicate. The returned model will indicate whether + * the collection belongs to the set defined by the predicate. + */ + public static PropertyValueModel<Boolean> booleanPropertyValueModel(CollectionValueModel<?> collectionModel, Predicate<? super Collection<?>> predicate) { + return propertyValueModel(collectionModel, TransformerTools.adapt(predicate)); + } + + + /** + * Construct a property value model adapted to the specified + * collection value model and transformer. + * @see PluggablePropertyValueModel + */ + public static <E, V> PropertyValueModel<V> propertyValueModel(CollectionValueModel<? extends E> collectionModel, Transformer<? super Collection<E>, V> transformer) { + return PropertyValueModelTools.propertyValueModel(pluggablePropertyValueModelAdapterFactory(collectionModel, transformer)); + } + + /** + * Construct a pluggable property value model adapter factory for the specified + * collection value model and transformer. + * @see PluggablePropertyValueModel + */ + public static <E, V> PluggablePropertyValueModel.Adapter.Factory<V> pluggablePropertyValueModelAdapterFactory(CollectionValueModel<? extends E> collectionModel, Transformer<? super Collection<E>, V> transformer) { + return new CollectionPluggablePropertyValueModelAdapter.Factory<>(collectionModel, transformer); + } + + /** + * Construct a <em>modifiable</em> property value model adapted to the specified + * collection value model that returns whether the collection belongs to the set defined + * by the specified predicate. + * This model will use the specified collection mutator to modify the collection value model. + */ + public static ModifiablePropertyValueModel<Boolean> booleanModifiablePropertyValueModel(CollectionValueModel<?> collectionModel, Predicate<? super Collection<?>> predicate, BooleanClosure.Adapter collectionMutator) { + return booleanModifiablePropertyValueModel(collectionModel, TransformerTools.adapt(predicate), collectionMutator); + } + + /** + * Construct a <em>modifiable</em> property value model adapter for the specified + * collection value model that returns whether the collection belongs to the set defined + * by the specified transformer. + * This model will use the specified collection mutator to modify the collection value model. + */ + public static ModifiablePropertyValueModel<Boolean> booleanModifiablePropertyValueModel(CollectionValueModel<?> collectionModel, Transformer<? super Collection<?>, Boolean> transformer, BooleanClosure.Adapter collectionMutator) { + PluggablePropertyValueModel.Adapter.Factory<Boolean> factory = pluggablePropertyValueModelAdapterFactory(collectionModel, transformer); + Closure<Boolean> closure = ClosureTools.booleanClosure(collectionMutator); + return PropertyValueModelTools.pluggableModifiablePropertyValueModel(factory, closure); + } + + + // ********** filtering ********** + + /** + * Construct a collection value model with the specified wrapped collection value model and filter. + */ + public static <E> CollectionValueModel<E> filter(CollectionValueModel<? extends E> collectionModel, Predicate<E> filter) { + return new FilteringCollectionValueModel<>(collectionModel, filter); + } + + + // ********** suppressed constructor ********** + + /** + * Suppress default constructor, ensuring non-instantiability. + */ + private CollectionValueModelTools() { + super(); + throw new UnsupportedOperationException(); + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelWrapper.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelWrapper.java index 4d36f09f93..377d290b57 100644 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelWrapper.java +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelWrapper.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2012 Oracle. All rights reserved. + * Copyright (c) 2007, 2016 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. @@ -21,6 +21,17 @@ import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; * another collection value model, "lazily" listen to it, and propagate * its change notifications. Subclasses must implement the appropriate * {@link CollectionValueModel}. + * <p> + * Subclasses must implement the following methods:<ul> + * <li>{@link #itemsAdded(CollectionAddEvent)}<p> + * implement this method to handle added items + * <li>{@link #itemsRemoved(CollectionRemoveEvent)}<p> + * implement this method to handle removed items + * <li>{@link #collectionCleared(CollectionClearEvent)}<p> + * implement this method to handle cleared collection + * <li>{@link #collectionChanged(CollectionChangeEvent)}<p> + * implement this method to handle changed collection + * </ul> * * @param <E> the type of elements held by the model */ diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositeBooleanPropertyValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositeBooleanPropertyValueModel.java deleted file mode 100644 index e032203172..0000000000 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositeBooleanPropertyValueModel.java +++ /dev/null @@ -1,350 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010, 2012 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.common.utility.internal.model.value; - -import java.util.Collection; -import org.eclipse.jpt.common.utility.internal.iterable.TransformationIterable; -import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; -import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; -import org.eclipse.jpt.common.utility.transformer.Transformer; - -/** - * A <code>CompositeBooleanPropertyValueModel</code> adapts a - * {@link CollectionValueModel} holding boolean {@link PropertyValueModel}s - * to a single boolean {@link PropertyValueModel}. The composite value is - * determined by the {@link CompositeBooleanPropertyValueModel.Adapter} provided - * at construction. Typically the composite will be either an AND or an OR of - * all the boolean values. - * <p> - * If there are <em>no</em> boolean {@link PropertyValueModel}s, the composite - * will return its default value; which, by default, is <code>null</code>. - * <p> - * <strong>NB:</strong> None of the wrapped boolean models can return a - * <code>null</code> value or this class will throw an exception. The assumption - * is that all the wrapped boolean models are configured with "default" values - * that determine the model's value (TRUE or FALSE) when <code>null</code>. - */ -public class CompositeBooleanPropertyValueModel - extends CompositePropertyValueModel<Boolean, Boolean> -{ - /** - * Calculation of the model's value is delegated to this adapter. - */ - protected final Adapter adapter; - - /** - * The default setting for the composite boolean property value model; - * for when the underlying collection model is empty. - * The default [default value] is <code>null</code>. - */ - private final Boolean defaultValue; - - - // ********** AND factory methods ********** - - /** - * Construct a boolean property value model that is a composite AND of the - * specified boolean property value models; i.e. the model's value is true - * if all the contained models are true, otherwise its value is false. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public static CompositeBooleanPropertyValueModel and(PropertyValueModel<Boolean>... array) { - return new CompositeBooleanPropertyValueModel(AND_ADAPTER, array); - } - - /** - * Construct a boolean property value model that is a composite AND of the - * specified boolean property value models; i.e. the model's value is true - * if all the contained models are true, otherwise its value is false. - * When the model contains no nested models, its value will be the - * specified default. - */ - public static CompositeBooleanPropertyValueModel and(Boolean defaultValue, PropertyValueModel<Boolean>... array) { - return new CompositeBooleanPropertyValueModel(AND_ADAPTER, defaultValue, array); - } - - /** - * Construct a boolean property value model that is a composite AND of the - * specified boolean property value models; i.e. the model's value is true - * if all the contained models are true, otherwise its value is false. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public static <E extends PropertyValueModel<Boolean>> CompositeBooleanPropertyValueModel and(Collection<E> collection) { - return new CompositeBooleanPropertyValueModel(AND_ADAPTER, collection); - } - - /** - * Construct a boolean property value model that is a composite AND of the - * specified boolean property value models; i.e. the model's value is true - * if all the contained models are true, otherwise its value is false. - * When the model contains no nested models, its value will be the - * specified default. - */ - public static <E extends PropertyValueModel<Boolean>> CompositeBooleanPropertyValueModel and(Boolean defaultValue, Collection<E> collection) { - return new CompositeBooleanPropertyValueModel(AND_ADAPTER, defaultValue, collection); - } - - /** - * Construct a boolean property value model that is a composite AND of the - * specified boolean property value models; i.e. the model's value is true - * if all the contained models are true, otherwise its value is false. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public static CompositeBooleanPropertyValueModel and(CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { - return new CompositeBooleanPropertyValueModel(AND_ADAPTER, collectionModel); - } - - /** - * Construct a boolean property value model that is a composite AND of the - * specified boolean property value models; i.e. the model's value is true - * if all the contained models are true, otherwise its value is false. - * When the model contains no nested models, its value will be the - * specified default. - */ - public static CompositeBooleanPropertyValueModel and(Boolean defaultValue, CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { - return new CompositeBooleanPropertyValueModel(AND_ADAPTER, defaultValue, collectionModel); - } - - - // ********** OR factory methods ********** - - /** - * Construct a boolean property value model that is a composite OR of the - * specified boolean property value models; i.e. the model's value is false - * if all the contained models are false, otherwise its value is true. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public static CompositeBooleanPropertyValueModel or(PropertyValueModel<Boolean>... array) { - return new CompositeBooleanPropertyValueModel(OR_ADAPTER, array); - } - - /** - * Construct a boolean property value model that is a composite OR of the - * specified boolean property value models; i.e. the model's value is false - * if all the contained models are false, otherwise its value is true. - * When the model contains no nested models, its value will be the - * specified default. - */ - public static CompositeBooleanPropertyValueModel or(Boolean defaultValue, PropertyValueModel<Boolean>... array) { - return new CompositeBooleanPropertyValueModel(OR_ADAPTER, defaultValue, array); - } - - /** - * Construct a boolean property value model that is a composite OR of the - * specified boolean property value models; i.e. the model's value is false - * if all the contained models are false, otherwise its value is true. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public static <E extends PropertyValueModel<Boolean>> CompositeBooleanPropertyValueModel or(Collection<E> collection) { - return new CompositeBooleanPropertyValueModel(OR_ADAPTER, collection); - } - - /** - * Construct a boolean property value model that is a composite OR of the - * specified boolean property value models; i.e. the model's value is false - * if all the contained models are false, otherwise its value is true. - * When the model contains no nested models, its value will be the - * specified default. - */ - public static <E extends PropertyValueModel<Boolean>> CompositeBooleanPropertyValueModel or(Boolean defaultValue, Collection<E> collection) { - return new CompositeBooleanPropertyValueModel(OR_ADAPTER, defaultValue, collection); - } - - /** - * Construct a boolean property value model that is a composite OR of the - * specified boolean property value models; i.e. the model's value is false - * if all the contained models are false, otherwise its value is true. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public static CompositeBooleanPropertyValueModel or(CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { - return new CompositeBooleanPropertyValueModel(OR_ADAPTER, collectionModel); - } - - /** - * Construct a boolean property value model that is a composite OR of the - * specified boolean property value models; i.e. the model's value is false - * if all the contained models are false, otherwise its value is true. - * When the model contains no nested models, its value will be the - * specified default. - */ - public static CompositeBooleanPropertyValueModel or(Boolean defaultValue, CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { - return new CompositeBooleanPropertyValueModel(OR_ADAPTER, defaultValue, collectionModel); - } - - - // ********** constructors ********** - - /** - * Construct a boolean property value model that is a composite of the specified - * boolean property value models, as defined by the specified adapter. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public CompositeBooleanPropertyValueModel(Adapter adapter, PropertyValueModel<Boolean>... array) { - this(adapter, null, array); - } - - /** - * Construct a boolean property value model that is a composite of the specified - * boolean property value models, as defined by the specified adapter. - * When the model contains no nested models, its value will be the - * specified default. - */ - public CompositeBooleanPropertyValueModel(Adapter adapter, Boolean defaultValue, PropertyValueModel<Boolean>... array) { - super(array); - if (adapter == null) { - throw new NullPointerException(); - } - this.adapter = adapter; - this.defaultValue = defaultValue; - } - - /** - * Construct a boolean property value model that is a composite of the specified - * boolean property value models, as defined by the specified adapter. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public <E extends PropertyValueModel<Boolean>> CompositeBooleanPropertyValueModel(Adapter adapter, Collection<E> collection) { - this(adapter, null, collection); - } - - /** - * Construct a boolean property value model that is a composite of the specified - * boolean property value models, as defined by the specified adapter. - * When the model contains no nested models, its value will be the - * specified default. - */ - public <E extends PropertyValueModel<Boolean>> CompositeBooleanPropertyValueModel(Adapter adapter, Boolean defaultValue, Collection<E> collection) { - super(collection); - if (adapter == null) { - throw new NullPointerException(); - } - this.adapter = adapter; - this.defaultValue = defaultValue; - } - - /** - * Construct a boolean property value model that is a composite of the specified - * boolean property value models, as defined by the specified adapter. - * The model's default value, when it contains no nested models, is - * <code>null</code>. - */ - public CompositeBooleanPropertyValueModel(Adapter adapter, CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { - this(adapter, null, collectionModel); - } - - /** - * Construct a boolean property value model that is a composite of the specified - * boolean property value models, as defined by the specified adapter. - * When the model contains no nested models, its value will be the - * specified default. - */ - public CompositeBooleanPropertyValueModel(Adapter adapter, Boolean defaultValue, CollectionValueModel<? extends PropertyValueModel<Boolean>> collectionModel) { - super(collectionModel); - if (adapter == null) { - throw new NullPointerException(); - } - this.adapter = adapter; - this.defaultValue = defaultValue; - } - - - // ********** implementation ********** - - /** - * Return the {@link #defaultValue} if the collection is empty; - * otherwise delegate to the {@link #adapter}. - */ - @Override - protected Boolean buildValue() { - return (this.collectionModel.size() == 0) ? this.defaultValue : this.buildValue_(); - } - - protected Boolean buildValue_() { - return this.adapter.buildValue(this.getBooleans()); - } - - protected Iterable<Boolean> getBooleans() { - return new TransformationIterable<PropertyValueModel<? extends Boolean>, Boolean>(this.collectionModel, BOOLEAN_TRANSFORMER); - } - - protected static final Transformer<PropertyValueModel<? extends Boolean>, Boolean> BOOLEAN_TRANSFORMER = new BooleanTransformer(); - protected static class BooleanTransformer - implements Transformer<PropertyValueModel<? extends Boolean>, Boolean> - { - public Boolean transform(PropertyValueModel<? extends Boolean> booleanModel) { - return booleanModel.getValue(); - } - @Override - public String toString() { - return this.getClass().getSimpleName(); - } - } - - - // ********** adapter ********** - - /** - * This adapter allows the {@link CompositeBooleanPropertyValueModel} to be - * pluggable. - */ - public interface Adapter { - /** - * Return the composite boolean value of the specified collection - * of booleans. - */ - Boolean buildValue(Iterable<Boolean> booleans); - } - - /** - * Return <code>true</code> if all the booleans are <code>true</code>; - * otherwise return <code>false</code>. - */ - public static final Adapter AND_ADAPTER = new Adapter() { - public Boolean buildValue(Iterable<Boolean> booleans) { - for (Boolean b : booleans) { - if ( ! b.booleanValue()) { - return Boolean.FALSE; - } - } - return Boolean.TRUE; - } - @Override - public String toString() { - return "AND_ADAPTER"; //$NON-NLS-1$ - } - }; - - /** - * Return <code>false</code> if all the booleans are <code>false</code>; - * otherwise return <code>true</code>. - */ - public static final Adapter OR_ADAPTER = new Adapter() { - public Boolean buildValue(Iterable<Boolean> booleans) { - for (Boolean b : booleans) { - if (b.booleanValue()) { - return Boolean.TRUE; - } - } - return Boolean.FALSE; - } - @Override - public String toString() { - return "OR_ADAPTER"; //$NON-NLS-1$ - } - }; -} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositePropertyValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositePropertyValueModel.java deleted file mode 100644 index 796c53b7d6..0000000000 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositePropertyValueModel.java +++ /dev/null @@ -1,197 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2009, 2012 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.common.utility.internal.model.value; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import org.eclipse.jpt.common.utility.internal.collection.IdentityHashBag; -import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent; -import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; -import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; -import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent; -import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; -import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter; -import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; -import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; -import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; - -/** - * A <code>CompositePropertyValueModel</code> adapts a - * {@link CollectionValueModel} holding other {@link PropertyValueModel}s - * to a single {@link PropertyValueModel}. - * <p> - * Subclasses must implement:<ul> - * <li>{@link #buildValue()}<p> - * to return the current property value, as derived from the - * component values - * </ul> - * <strong>NB:</strong> The wrapped collection must not contain any duplicates - * or this class will throw an exception. - * - * @param <V> the type of the model's value - * @param <E> the type of the wrapped collection value model's - * property value model's values - */ -public abstract class CompositePropertyValueModel<V, E> - extends CollectionPropertyValueModelAdapter<V, PropertyValueModel<? extends E>> -{ - /** - * Cache the component property value models so we can stop listening to - * them when they are removed from the collection value model. - */ - protected final IdentityHashBag<PropertyValueModel<? extends E>> componentPVMs = - new IdentityHashBag<PropertyValueModel<? extends E>>(); - - /** - * Listen to every property value model in the collection value model. - * If one changes, we need to re-calculate our value. - */ - protected final PropertyChangeListener componentListener; - - - // ********** constructors ********** - - /** - * Construct a property value model that is a composite of the specified - * property value models. - */ - public CompositePropertyValueModel(PropertyValueModel<? extends E>... collection) { - this(Arrays.asList(collection)); - } - - /** - * Construct a property value model that is a composite of the specified - * property value models. - */ - public <P extends PropertyValueModel<? extends E>> CompositePropertyValueModel(Collection<? extends P> collection) { - this(new StaticCollectionValueModel<P>(collection)); - } - - /** - * Construct a property value model that is a composite of the specified - * property value models. - */ - public <P extends PropertyValueModel<? extends E>> CompositePropertyValueModel(CollectionValueModel<P> collectionModel) { - super(collectionModel); - this.componentListener = this.buildComponentListener(); - } - - - // ********** initialization ********** - - protected PropertyChangeListener buildComponentListener() { - return new ComponentListener(); - } - - protected class ComponentListener - extends PropertyChangeAdapter - { - @Override - public void propertyChanged(PropertyChangeEvent event) { - CompositePropertyValueModel.this.componentChanged(event); - } - } - - - // ********** behavior ********** - - /** - * Subclasses can override this method if the event can be used to improve - * the performance of building a new value (e.g. some property changes may - * not necessitate the re-calculation of the value). - */ - protected void componentChanged(@SuppressWarnings("unused") PropertyChangeEvent event) { - this.propertyChanged(); - } - - - // ********** CollectionPropertyValueModelAdapter overrides ********** - - @Override - protected void engageModel_() { - super.engageModel_(); - this.addComponentPVMs(this.collectionModel); - } - - protected <P extends PropertyValueModel<? extends E>> void addComponentPVMs(Iterable<P> pvms) { - for (P each : pvms) { - this.componentPVMs.add(each); - each.addPropertyChangeListener(VALUE, this.componentListener); - } - } - - @Override - protected void disengageModel_() { - this.removeComponentPVMs(this.collectionModel); - super.disengageModel_(); - } - - protected <P extends PropertyValueModel<? extends E>> void removeComponentPVMs(Iterable<P> pvms) { - for (P each : pvms) { - each.removePropertyChangeListener(VALUE, this.componentListener); - this.componentPVMs.remove(each); - } - } - - @Override - protected void itemsAdded(CollectionAddEvent event) { - this.addComponentPVMs(this.getItems(event)); - super.itemsAdded(event); - } - - @Override - protected void itemsRemoved(CollectionRemoveEvent event) { - this.removeComponentPVMs(this.getItems(event)); - super.itemsRemoved(event); - } - - @Override - protected void collectionCleared(CollectionClearEvent event) { - this.removeAllComponentPVMs(); - super.collectionCleared(event); - } - - protected void removeAllComponentPVMs() { - // copy the list so we don't eat our own tail - ArrayList<PropertyValueModel<? extends E>> copy = new ArrayList<PropertyValueModel<? extends E>>(this.componentPVMs); - this.removeComponentPVMs(copy); - } - - @Override - protected void collectionChanged(CollectionChangeEvent event) { - this.removeAllComponentPVMs(); - this.addComponentPVMs(this.collectionModel); - super.collectionChanged(event); - } - - - // ********** convenience methods ********** - - /** - * Our constructor accepts only a - * {@link CollectionValueModel}{@code<? extends }{@link PropertyValueModel}{@code<? extends E>>}. - */ - // minimize scope of suppressed warnings - @SuppressWarnings("unchecked") - protected Iterable<? extends PropertyValueModel<? extends E>> getItems(CollectionAddEvent event) { - return (Iterable<? extends PropertyValueModel<? extends E>>) event.getItems(); - } - - /** - * Our constructor accepts only a - * {@link CollectionValueModel}{@code<? extends }{@link PropertyValueModel}{@code<? extends E>>}. - */ - // minimize scope of suppressed warnings - @SuppressWarnings("unchecked") - protected Iterable<? extends PropertyValueModel<? extends E>> getItems(CollectionRemoveEvent event) { - return (Iterable<? extends PropertyValueModel<? extends E>>) event.getItems(); - } -} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositePropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositePropertyValueModelAdapter.java new file mode 100644 index 0000000000..3ae453b06d --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositePropertyValueModelAdapter.java @@ -0,0 +1,266 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.IdentityHashMap; +import org.eclipse.jpt.common.utility.internal.ObjectTools; +import org.eclipse.jpt.common.utility.internal.collection.ListTools; +import org.eclipse.jpt.common.utility.internal.model.value.PluggablePropertyValueModel.Adapter; +import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent; +import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; +import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; +import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.CollectionChangeListener; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; +import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.transformer.Transformer; + +/** + * Adapt a {@link CollectionValueModel collection value model} holding + * {@link PropertyValueModel property value models} + * to a single {@link PropertyValueModel property value model}, sorta. + * <p> + * This adapter is constructed with a {@link CollectionValueModel + * collection value model} and a {@link Transformer transformer} that can + * transform the collection's property value models' values to a single value. + * <p> + * This is an adapter that can be plugged into a {@link PluggablePropertyValueModel}. + * <p> + * <strong>NB:</strong> The wrapped collection value model must not contain any + * <code>null</code>s or duplicate property value models. + * + * @param <E> the type of the adapted collection value model's + * property value models' values + * @param <V> the type of the model's derived value + * + * @see PluggablePropertyValueModel + * @see CollectionPluggablePropertyValueModelAdapter + */ +public final class CompositePropertyValueModelAdapter<E, V> + implements PluggablePropertyValueModel.Adapter<V>, CollectionChangeListener +{ + private final Factory<E, V> factory; + + /** + * The <em>real</em> adapter, passed to us as a listener. + */ + private final AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener; + + /** + * Listen to every property value model in the collection value model. + * If one changes, we need to re-calculate {@link #value} + * and notify @{link #listener}. + */ + private final PropertyChangeListener componentListener; + + /** + * Cached copy of {@link Factory#collectionModel}'s elements + * and their values. + */ + private final IdentityHashMap<PropertyValueModel<? extends E>, E> values; + + /** + * Protects {@link #values} from {@link Factory#transformer}. + */ + private final Collection<E> unmodifiableValues; + + /** + * The derived value. + */ + private volatile V value; + + + // ********** constructor ********** + + public CompositePropertyValueModelAdapter(Factory<E, V> factory, AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + super(); + if (factory == null) { + throw new NullPointerException(); + } + this.factory = factory; + if (listener == null) { + throw new NullPointerException(); + } + this.listener = listener; + this.componentListener = this.buildComponentListener(); + this.values = new IdentityHashMap<>(); + this.unmodifiableValues = Collections.unmodifiableCollection(this.values.values()); + } + + + // ********** PropertyValueModelAdapter.Adapter ********** + + public V getValue() { + return this.value; + } + + public void engageModel() { + this.factory.collectionModel.addCollectionChangeListener(CollectionValueModel.VALUES, this); + this.addComponentPVMs(this.factory.collectionModel); + this.value = this.buildValue(); + } + + public void disengageModel() { + this.value = null; + this.removeComponentPVMs(this.factory.collectionModel); + if ( ! this.values.isEmpty()) { + throw new IllegalStateException("extraneous values: " + this.values); //$NON-NLS-1$ + } + this.factory.collectionModel.removeCollectionChangeListener(CollectionValueModel.VALUES, this); + } + + + // ********** CollectionChangeListener ********** + + @SuppressWarnings("unchecked") + public void itemsAdded(CollectionAddEvent event) { + this.addComponentPVMs((Iterable<? extends PropertyValueModel<? extends E>>) event.getItems()); + this.update(); + } + + @SuppressWarnings("unchecked") + public void itemsRemoved(CollectionRemoveEvent event) { + this.removeComponentPVMs((Iterable<? extends PropertyValueModel<? extends E>>) event.getItems()); + this.update(); + } + + public void collectionCleared(CollectionClearEvent event) { + this.removeCachedPVMs(); + this.update(); + } + + @SuppressWarnings("unchecked") + public void collectionChanged(CollectionChangeEvent event) { + this.removeCachedPVMs(); + this.addComponentPVMs((Iterable<? extends PropertyValueModel<? extends E>>) event.getCollection()); + this.update(); + } + + + // ********** add/remove component PVMs ********** + + private void addComponentPVMs(Iterable<? extends PropertyValueModel<? extends E>> pvms) { + for (PropertyValueModel<? extends E> pvm : pvms) { + this.addComponentPVM(pvm); + } + } + + private void addComponentPVM(PropertyValueModel<? extends E> pvm) { + if (pvm == null) { + throw new NullPointerException(); + } + if (this.values.containsKey(pvm)) { + throw new IllegalStateException("duplicate component: " + pvm); //$NON-NLS-1$ + } + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, this.componentListener); + this.values.put(pvm, pvm.getValue()); + } + + private void removeCachedPVMs() { + // copy the list so we don't eat our own tail + ArrayList<PropertyValueModel<? extends E>> copy = ListTools.arrayList(this.values.keySet()); + this.removeComponentPVMs(copy); + } + + private void removeComponentPVMs(Iterable<? extends PropertyValueModel<? extends E>> pvms) { + for (PropertyValueModel<? extends E> pvm : pvms) { + this.removeComponentPVM(pvm); + } + } + + private void removeComponentPVM(PropertyValueModel<? extends E> pvm) { + if ( ! this.values.containsKey(pvm)) { + throw new IllegalStateException("missing component: " + pvm); //$NON-NLS-1$ + } + this.values.remove(pvm); + pvm.removePropertyChangeListener(PropertyValueModel.VALUE, this.componentListener); + } + + + // ********** misc ********** + + private void update() { + this.listener.valueChanged(this.value = this.buildValue()); + } + + private V buildValue() { + return this.factory.transformer.transform(this.unmodifiableValues); + } + + @Override + public String toString() { + return ObjectTools.toString(this, this.value); + } + + + // ********** component listener ********** + + private PropertyChangeListener buildComponentListener() { + return new ComponentListener(); + } + + /* CU private */ class ComponentListener + extends PropertyChangeAdapter + { + @Override + public void propertyChanged(PropertyChangeEvent event) { + CompositePropertyValueModelAdapter.this.componentChanged(event); + } + } + + /* CU private */ void componentChanged(PropertyChangeEvent event) { + @SuppressWarnings("unchecked") + PropertyValueModel<? extends E> source = (PropertyValueModel<? extends E>) event.getSource(); + if ( ! this.values.containsKey(source)) { + throw new IllegalStateException("invalid component: " + source); //$NON-NLS-1$ + } + @SuppressWarnings("unchecked") + E newValue = (E) event.getNewValue(); + this.values.put(source, newValue); + this.update(); + } + + + // ********** PluggablePropertyValueModel.Adapter.Factory ********** + + public static class Factory<E, V> + implements PluggablePropertyValueModel.Adapter.Factory<V> + { + /* CU private */ final CollectionValueModel<? extends PropertyValueModel<? extends E>> collectionModel; + /* CU private */ final Transformer<? super Collection<E>, V> transformer; + + public Factory(CollectionValueModel<? extends PropertyValueModel<? extends E>> collectionModel, Transformer<? super Collection<E>, V> transformer) { + super(); + if (collectionModel == null) { + throw new NullPointerException(); + } + this.collectionModel = collectionModel; + if (transformer == null) { + throw new NullPointerException(); + } + this.transformer = transformer; + } + + public Adapter<V> buildAdapter(AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + return new CompositePropertyValueModelAdapter<>(this, listener); + } + + @Override + public String toString() { + return ObjectTools.toString(this); + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ElementPropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ElementPropertyValueModelAdapter.java deleted file mode 100644 index 8cc9d6f8ae..0000000000 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ElementPropertyValueModelAdapter.java +++ /dev/null @@ -1,131 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2011, 2013 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.common.utility.internal.model.value; - -import org.eclipse.jpt.common.utility.internal.ObjectTools; -import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; -import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; -import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; -import org.eclipse.jpt.common.utility.predicate.Predicate; - -/** - * Adapt an element in a collection value model to a property value model. - * The property model's value is determined by whether the collection model - * contains the value: If the collection model contains the value, - * the property model's value is <em>that</em> element; otherwise, the property - * model's value is <code>null</code>. A {@link #predicate} is used to determine - * whether the collection model contains the relevant value. - * <p> - * This is useful for a client (e.g. a UI widget) that is longer-living than its - * underlying model. Obviously, the client must be prepared to handle a value of - * <code>null</code>. - * - * @param <V> the type of the both the model's value and - * the wrapped collection value model's elements - */ -public class ElementPropertyValueModelAdapter<V> - extends CollectionPropertyValueModelAdapter<V, V> -{ - /** - * A predicate used to determine whether an element in the wrapped - * collection model is the model's value. - */ - protected final Predicate<V> predicate; - - - /** - * Construct a property value model whose value depends on whether the - * specified collection value model contains the value. The specified - * filter is used to determine whether an element in the specified - * collection model is the property value. - */ - public ElementPropertyValueModelAdapter(CollectionValueModel<? extends V> collectionModel, Predicate<V> predicate) { - super(collectionModel); - if (predicate == null) { - throw new NullPointerException(); - } - this.predicate = predicate; - } - - /** - * If the collection model contains the property model's {@link #value}, - * return that element; otherwise return <code>null</code>. - */ - @Override - protected V buildValue() { - for (V each : this.collectionModel) { - if (this.predicate.evaluate(each)) { - return each; - } - } - return null; - } - - /** - * Check whether the wrapped collection model now contains the - * {@link #value}. - */ - @Override - protected void itemsAdded(Iterable<V> items) { - if (this.value == null) { - this.itemsAdded_(items); - } - } - - protected void itemsAdded_(Iterable<V> items) { - for (V each : items) { - if (this.predicate.evaluate(each)) { - this.firePropertyChanged(VALUE, null, this.value = each); - return; - } - } - } - - /** - * Check whether the wrapped collection model no longer contains the - * {@link #value}. - */ - @Override - protected void itemsRemoved(Iterable<V> items) { - if (this.value != null) { - this.itemsRemoved_(items); - } - } - - protected void itemsRemoved_(Iterable<V> items) { - for (V each : items) { - if (ObjectTools.equals(each, this.value)) { - V old = this.value; - this.firePropertyChanged(VALUE, old, this.value = null); - return; - } - } - } - - /** - * The {@link #value} must now be <code>null</code>. - */ - @Override - protected void collectionCleared(CollectionClearEvent event) { - if (this.value != null) { - V old = this.value; - this.firePropertyChanged(VALUE, old, this.value = null); - } - } - - /** - * Re-calculate the {@link #value}. - */ - @Override - protected void collectionChanged(CollectionChangeEvent event) { - V old = this.value; - this.firePropertyChanged(VALUE, old, this.value = this.buildValue()); - } -} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/FilteringCollectionValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/FilteringCollectionValueModel.java index ca51df9aae..95bf6e8abd 100644 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/FilteringCollectionValueModel.java +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/FilteringCollectionValueModel.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2013 Oracle. All rights reserved. + * Copyright (c) 2007, 2016 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. @@ -9,18 +9,16 @@ ******************************************************************************/ package org.eclipse.jpt.common.utility.internal.model.value; -import java.util.ArrayList; import java.util.Iterator; import org.eclipse.jpt.common.utility.internal.collection.CollectionTools; +import org.eclipse.jpt.common.utility.internal.collection.HashBag; import org.eclipse.jpt.common.utility.internal.iterable.IterableTools; import org.eclipse.jpt.common.utility.internal.iterator.IteratorTools; -import org.eclipse.jpt.common.utility.internal.predicate.PredicateTools; import org.eclipse.jpt.common.utility.model.event.CollectionAddEvent; import org.eclipse.jpt.common.utility.model.event.CollectionChangeEvent; import org.eclipse.jpt.common.utility.model.event.CollectionClearEvent; import org.eclipse.jpt.common.utility.model.event.CollectionRemoveEvent; import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; -import org.eclipse.jpt.common.utility.model.value.ListValueModel; import org.eclipse.jpt.common.utility.predicate.Predicate; /** @@ -48,25 +46,17 @@ public class FilteringCollectionValueModel<E> extends CollectionValueModelWrapper<E> implements CollectionValueModel<E> { - /** This filters the items in the nested collection. */ - private Predicate<E> filter; + /** This filters the items in the wrapped collection. */ + private volatile Predicate<E> filter; /** Cache the items that were accepted by the filter */ - private final ArrayList<E> filteredItems = new ArrayList<E>(); + private final HashBag<E> filteredItems = new HashBag<>(); // ********** constructors ********** /** * Construct a collection value model with the specified wrapped - * collection value model and a filter that simply accepts every object. - */ - public FilteringCollectionValueModel(CollectionValueModel<? extends E> collectionModel) { - this(collectionModel, PredicateTools.<E>true_()); - } - - /** - * Construct a collection value model with the specified wrapped * collection value model and filter. */ public FilteringCollectionValueModel(CollectionValueModel<? extends E> collectionModel, Predicate<E> filter) { @@ -77,22 +67,6 @@ public class FilteringCollectionValueModel<E> this.filter = filter; } - /** - * Construct a collection value model with the specified wrapped - * list value model and a filter that simply accepts every object. - */ - public FilteringCollectionValueModel(ListValueModel<? extends E> listModel) { - this(new ListCollectionValueModelAdapter<E>(listModel)); - } - - /** - * Construct a collection value model with the specified wrapped - * list value model and filter. - */ - public FilteringCollectionValueModel(ListValueModel<? extends E> listModel, Predicate<E> filter) { - this(new ListCollectionValueModelAdapter<E>(listModel), filter); - } - // ********** CollectionValueModel implementation ********** @@ -150,6 +124,13 @@ public class FilteringCollectionValueModel<E> // ********** miscellaneous ********** /** + * Return the current filter. + */ + public Predicate<E> getFilter() { + return this.filter; + } + + /** * Change the filter and rebuild the collection. */ public void setFilter(Predicate<E> filter) { diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListCompositePropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListCompositePropertyValueModelAdapter.java new file mode 100644 index 0000000000..48804c2f1c --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListCompositePropertyValueModelAdapter.java @@ -0,0 +1,284 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.eclipse.jpt.common.utility.Association; +import org.eclipse.jpt.common.utility.internal.ObjectTools; +import org.eclipse.jpt.common.utility.internal.SimpleAssociation; +import org.eclipse.jpt.common.utility.internal.collection.ListTools; +import org.eclipse.jpt.common.utility.internal.model.value.PluggablePropertyValueModel.Adapter; +import org.eclipse.jpt.common.utility.model.event.ListAddEvent; +import org.eclipse.jpt.common.utility.model.event.ListChangeEvent; +import org.eclipse.jpt.common.utility.model.event.ListClearEvent; +import org.eclipse.jpt.common.utility.model.event.ListMoveEvent; +import org.eclipse.jpt.common.utility.model.event.ListRemoveEvent; +import org.eclipse.jpt.common.utility.model.event.ListReplaceEvent; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.ListChangeListener; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; +import org.eclipse.jpt.common.utility.model.value.ListValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.transformer.Transformer; + +/** + * Adapt a {@link ListValueModel list value model} holding + * {@link PropertyValueModel property value models} + * to a single {@link PropertyValueModel property value model}, sorta. + * <p> + * This adapter is constructed with a {@link ListValueModel + * list value model} and a {@link Transformer transformer} that can + * transform the list's property value models' values to a single value. + * <p> + * This is an adapter that can be plugged into a {@link PluggablePropertyValueModel}. + * <p> + * <strong>NB:</strong> The wrapped list value model must not contain any + * <code>null</code>s or duplicate property value models. + * + * @param <E> the type of the adapted list value model's + * property value models' values + * @param <V> the type of the model's derived value + * + * @see PluggablePropertyValueModel + * @see ListPluggablePropertyValueModelAdapter + */ +public final class ListCompositePropertyValueModelAdapter<E, V> + implements PluggablePropertyValueModel.Adapter<V>, ListChangeListener +{ + private final Factory<E, V> factory; + + /** + * The <em>real</em> adapter, passed to us as a listener. + */ + private final AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener; + + /** + * Listen to every property value model in the list value model. + * If one changes, we need to re-calculate {@link #value} + * and notify @{link #listener}. + */ + private final PropertyChangeListener componentListener; + + /** + * Cached copy of {@link Factory#listModel}'s elements' values. + */ + private final ArrayList<SimpleAssociation<PropertyValueModel<? extends E>, E>> values; + + /** + * The derived value. + */ + private volatile V value; + + + // ********** constructor ********** + + public ListCompositePropertyValueModelAdapter(Factory<E, V> factory, AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + super(); + if (factory == null) { + throw new NullPointerException(); + } + this.factory = factory; + if (listener == null) { + throw new NullPointerException(); + } + this.listener = listener; + this.componentListener = this.buildComponentListener(); + this.values = new ArrayList<>(); + } + + + // ********** PropertyValueModelAdapter.Adapter ********** + + public V getValue() { + return this.value; + } + + public void engageModel() { + this.factory.listModel.addListChangeListener(ListValueModel.LIST_VALUES, this); + this.addComponentPVMs(0, this.factory.listModel); + this.value = this.buildValue(); + } + + public void disengageModel() { + this.value = null; + this.removeComponentPVMs(0, this.factory.listModel.size(), this.factory.listModel); + if ( ! this.values.isEmpty()) { + throw new IllegalStateException("extraneous values: " + this.values); //$NON-NLS-1$ + } + this.factory.listModel.removeListChangeListener(ListValueModel.LIST_VALUES, this); + } + + + // ********** ListChangeListener ********** + + @SuppressWarnings("unchecked") + public void itemsAdded(ListAddEvent event) { + this.addComponentPVMs(event.getIndex(), (Iterable<? extends PropertyValueModel<? extends E>>) event.getItems()); + this.update(); + } + + @SuppressWarnings("unchecked") + public void itemsRemoved(ListRemoveEvent event) { + this.removeComponentPVMs(event.getIndex(), event.getItemsSize(), (Iterable<? extends PropertyValueModel<? extends E>>) event.getItems()); + this.update(); + } + + public void itemsMoved(ListMoveEvent event) { + ListTools.move(this.values, event.getTargetIndex(), event.getSourceIndex(), event.getLength()); + this.update(); + } + + @SuppressWarnings("unchecked") + public void itemsReplaced(ListReplaceEvent event) { + this.removeComponentPVMs(event.getIndex(), event.getItemsSize(), (Iterable<? extends PropertyValueModel<? extends E>>) event.getOldItems()); + this.addComponentPVMs(event.getIndex(), (Iterable<? extends PropertyValueModel<? extends E>>) event.getNewItems()); + this.update(); + } + + public void listCleared(ListClearEvent event) { + this.removeCachedPVMs(); + this.update(); + } + + @SuppressWarnings("unchecked") + public void listChanged(ListChangeEvent event) { + this.removeCachedPVMs(); + this.addComponentPVMs(0, (Iterable<? extends PropertyValueModel<? extends E>>) event.getList()); + this.update(); + } + + + // ********** add/remove component PVMs ********** + + private void addComponentPVMs(int index, Iterable<? extends PropertyValueModel<? extends E>> pvms) { + for (PropertyValueModel<? extends E> pvm : pvms) { + this.addComponentPVM(index++, pvm); + } + } + + private void addComponentPVM(int index, PropertyValueModel<? extends E> pvm) { + if (pvm == null) { + throw new NullPointerException(); + } + for (SimpleAssociation<PropertyValueModel<? extends E>, E> each : this.values) { + if (each.getKey() == pvm) { + throw new IllegalStateException("duplicate component: " + pvm); //$NON-NLS-1$ + } + } + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, this.componentListener); + this.values.add(index, new SimpleAssociation<>(pvm, pvm.getValue())); + } + + private void removeCachedPVMs() { + @SuppressWarnings("unchecked") + Transformer<SimpleAssociation<PropertyValueModel<? extends E>, E>, PropertyValueModel<? extends E>> transformer = Association.KEY_TRANSFORMER; + this.removeComponentPVMs(this.values, ListTools.transform(this.values, transformer)); + } + + private void removeComponentPVMs(int index, int length, Iterable<? extends PropertyValueModel<? extends E>> expectedPVMs) { + this.removeComponentPVMs(this.values.subList(index, index + length), expectedPVMs); + } + + private void removeComponentPVMs(List<SimpleAssociation<PropertyValueModel<? extends E>, E>> subList, Iterable<? extends PropertyValueModel<? extends E>> expectedPVMs) { + Iterator<? extends PropertyValueModel<? extends E>> stream = expectedPVMs.iterator(); + for (SimpleAssociation<PropertyValueModel<? extends E>, E> each : subList) { + PropertyValueModel<? extends E> pvm = each.getKey(); + PropertyValueModel<? extends E> expectedPVM = stream.next(); + if (pvm != expectedPVM) { + throw new IllegalStateException("inconsistent component: " + pvm + " - expected: " + expectedPVM); //$NON-NLS-1$ //$NON-NLS-2$ + } + pvm.removePropertyChangeListener(PropertyValueModel.VALUE, this.componentListener); + } + subList.clear(); + } + + + // ********** misc ********** + + private void update() { + this.listener.valueChanged(this.value = this.buildValue()); + } + + private V buildValue() { + @SuppressWarnings("unchecked") + Transformer<SimpleAssociation<PropertyValueModel<? extends E>, E>, E> transformer = Association.VALUE_TRANSFORMER; + return this.factory.transformer.transform(ListTools.transform(this.values, transformer)); + } + + @Override + public String toString() { + return ObjectTools.toString(this, this.value); + } + + + // ********** component listener ********** + + private PropertyChangeListener buildComponentListener() { + return new ComponentListener(); + } + + /* CU private */ class ComponentListener + extends PropertyChangeAdapter + { + @Override + public void propertyChanged(PropertyChangeEvent event) { + ListCompositePropertyValueModelAdapter.this.componentChanged(event); + } + } + + /* CU private */ void componentChanged(PropertyChangeEvent event) { + @SuppressWarnings("unchecked") + PropertyValueModel<? extends E> source = (PropertyValueModel<? extends E>) event.getSource(); + for (SimpleAssociation<PropertyValueModel<? extends E>, E> each : this.values) { + if (each.getKey() == source) { + @SuppressWarnings("unchecked") + E newValue = (E) event.getNewValue(); + each.setValue(newValue); + this.update(); + return; + } + } + throw new IllegalStateException("invalid component: " + source); //$NON-NLS-1$ + } + + + // ********** PluggablePropertyValueModel.Adapter.Factory ********** + + public static class Factory<E, V> + implements PluggablePropertyValueModel.Adapter.Factory<V> + { + /* CU private */ final ListValueModel<? extends PropertyValueModel<? extends E>> listModel; + /* CU private */ final Transformer<? super List<E>, V> transformer; + + public Factory(ListValueModel<? extends PropertyValueModel<? extends E>> listModel, Transformer<? super List<E>, V> transformer) { + super(); + if (listModel == null) { + throw new NullPointerException(); + } + this.listModel = listModel; + if (transformer == null) { + throw new NullPointerException(); + } + this.transformer = transformer; + } + + public Adapter<V> buildAdapter(AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + return new ListCompositePropertyValueModelAdapter<>(this, listener); + } + + @Override + public String toString() { + return ObjectTools.toString(this); + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListPluggablePropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListPluggablePropertyValueModelAdapter.java new file mode 100644 index 0000000000..d1c4eb1536 --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListPluggablePropertyValueModelAdapter.java @@ -0,0 +1,189 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import org.eclipse.jpt.common.utility.internal.ObjectTools; +import org.eclipse.jpt.common.utility.internal.collection.CollectionTools; +import org.eclipse.jpt.common.utility.internal.collection.ListTools; +import org.eclipse.jpt.common.utility.internal.model.value.PluggablePropertyValueModel.Adapter; +import org.eclipse.jpt.common.utility.model.event.ListAddEvent; +import org.eclipse.jpt.common.utility.model.event.ListChangeEvent; +import org.eclipse.jpt.common.utility.model.event.ListClearEvent; +import org.eclipse.jpt.common.utility.model.event.ListMoveEvent; +import org.eclipse.jpt.common.utility.model.event.ListRemoveEvent; +import org.eclipse.jpt.common.utility.model.event.ListReplaceEvent; +import org.eclipse.jpt.common.utility.model.listener.ListChangeListener; +import org.eclipse.jpt.common.utility.model.value.ListValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.transformer.Transformer; + +/** + * Adapt a {@link ListValueModel list value model} to + * a {@link PropertyValueModel property value model}, sorta. + * <p> + * This adapter is constructed with a {@link ListValueModel + * list value model} and a {@link Transformer transformer} that can + * transform the list to a single value. + * <p> + * This is an adapter that can be plugged into a {@link PluggablePropertyValueModel}. + * + * @param <E> the type of the adapted list value model's elements + * @param <V> the type of the model's derived value + * + * @see PluggablePropertyValueModel + */ +public final class ListPluggablePropertyValueModelAdapter<E, V> + implements PluggablePropertyValueModel.Adapter<V>, ListChangeListener +{ + private final Factory<E, V> factory; + + /** The <em>real</em> adapter. */ + private final AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener; + + /** Cached copy of model's elements. */ + private final ArrayList<E> list; + + /** Protects {@link #list} from {@link Factory#transformer}. */ + private final List<E> unmodifiableList; + + /** The derived value. */ + private volatile V value; + + + // ********** constructors ********** + + public ListPluggablePropertyValueModelAdapter(Factory<E, V> factory, AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + super(); + if (factory == null) { + throw new NullPointerException(); + } + this.factory = factory; + if (listener == null) { + throw new NullPointerException(); + } + this.listener = listener; + this.list = new ArrayList<>(); + this.unmodifiableList = Collections.unmodifiableList(this.list); + } + + + // ********** PropertyValueModelAdapter.Adapter ********** + + public V getValue() { + return this.value; + } + + public void engageModel() { + this.factory.listModel.addListChangeListener(ListValueModel.LIST_VALUES, this); + ListTools.addAll(this.list, 0, this.factory.listModel); + this.value = this.buildValue(); + } + + public void disengageModel() { + this.value = null; + this.list.clear(); + this.factory.listModel.removeListChangeListener(ListValueModel.LIST_VALUES, this); + } + + + // ********** ListChangeListener ********** + + @SuppressWarnings("unchecked") + public void itemsAdded(ListAddEvent event) { + ListTools.addAll(this.list, event.getIndex(), (Iterable<E>) event.getItems(), event.getItemsSize()); + this.update(); + } + + public void itemsRemoved(ListRemoveEvent event) { + ListTools.removeElementsAtIndex(this.list, event.getIndex(), event.getItemsSize()); + this.update(); + } + + public void itemsReplaced(ListReplaceEvent event) { + @SuppressWarnings("unchecked") + Iterable<E> newItems = (Iterable<E>) event.getNewItems(); + Iterator<E> stream = newItems.iterator(); + int last = event.getIndex() + event.getItemsSize(); + for (int i = event.getIndex(); i < last; i++) { + this.list.set(i, stream.next()); + } + this.update(); + } + + public void itemsMoved(ListMoveEvent event) { + ListTools.move(this.list, event.getTargetIndex(), event.getSourceIndex(), event.getLength()); + this.update(); + } + + public void listCleared(ListClearEvent event) { + this.list.clear(); + this.update(); + } + + @SuppressWarnings("unchecked") + public void listChanged(ListChangeEvent event) { + this.list.clear(); + CollectionTools.addAll(this.list, (Iterable<E>) event.getList()); + this.update(); + } + + + // ********** misc ********** + + private void update() { + V newValue = this.buildValue(); + this.value = newValue; + this.listener.valueChanged(newValue); + } + + private V buildValue() { + return this.factory.transformer.transform(this.unmodifiableList); + } + + @Override + public String toString() { + return ObjectTools.toString(this, this.value); + } + + + // ********** PluggablePropertyValueModel.Adapter.Factory ********** + + public static class Factory<E, V> + implements PluggablePropertyValueModel.Adapter.Factory<V> + { + /* CU private */ final ListValueModel<? extends E> listModel; + /* CU private */ final Transformer<? super List<E>, V> transformer; + + public Factory(ListValueModel<? extends E> listModel, Transformer<? super List<E>, V> transformer) { + super(); + if (listModel == null) { + throw new NullPointerException(); + } + this.listModel = listModel; + if (transformer == null) { + throw new NullPointerException(); + } + this.transformer = transformer; + } + + public Adapter<V> buildAdapter(AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + return new ListPluggablePropertyValueModelAdapter<>(this, listener); + } + + @Override + public String toString() { + return ObjectTools.toString(this); + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListPropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListPropertyValueModelAdapter.java deleted file mode 100644 index de238160c9..0000000000 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListPropertyValueModelAdapter.java +++ /dev/null @@ -1,176 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2008, 2012 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.common.utility.internal.model.value; - -import org.eclipse.jpt.common.utility.model.event.ListAddEvent; -import org.eclipse.jpt.common.utility.model.event.ListChangeEvent; -import org.eclipse.jpt.common.utility.model.event.ListClearEvent; -import org.eclipse.jpt.common.utility.model.event.ListMoveEvent; -import org.eclipse.jpt.common.utility.model.event.ListRemoveEvent; -import org.eclipse.jpt.common.utility.model.event.ListReplaceEvent; -import org.eclipse.jpt.common.utility.model.listener.ListChangeAdapter; -import org.eclipse.jpt.common.utility.model.listener.ListChangeListener; -import org.eclipse.jpt.common.utility.model.value.ListValueModel; - -/** - * This abstract class provides the infrastructure needed to wrap - * a list value model, "lazily" listen to it, and convert - * its change notifications into property value model change - * notifications. - * <p> - * Subclasses must override:<ul> - * <li>{@link #buildValue()}<p> - * to return the current property value, as derived from the - * current list value - * </ul> - * Subclasses might want to override the following methods - * to improve performance (by not recalculating the value, if possible):<ul> - * <li>{@link #itemsAdded(ListAddEvent event)} - * <li>{@link #itemsRemoved(ListRemoveEvent event)} - * <li>{@link #itemsReplaced(ListReplaceEvent event)} - * <li>{@link #itemsMoved(ListMoveEvent event)} - * <li>{@link #listCleared(ListClearEvent event)} - * <li>{@link #listChanged(ListChangeEvent event)} - * </ul> - */ -public abstract class ListPropertyValueModelAdapter<T> - extends AbstractPropertyValueModelAdapter<T> -{ - /** The wrapped list value model. */ - protected final ListValueModel<?> listModel; - - /** A listener that allows us to sync with changes to the wrapped list model. */ - protected final ListChangeListener listListener; - - - // ********** constructor/initialization ********** - - /** - * Construct a property value model with the specified wrapped - * list value model. - */ - protected ListPropertyValueModelAdapter(ListValueModel<?> listModel) { - super(); - if (listModel == null) { - throw new NullPointerException(); - } - this.listModel = listModel; - this.listListener = this.buildListListener(); - } - - protected ListChangeListener buildListListener() { - return new ListListener(); - } - - protected class ListListener - extends ListChangeAdapter - { - @Override - public void itemsAdded(ListAddEvent event) { - ListPropertyValueModelAdapter.this.itemsAdded(event); - } - @Override - public void itemsRemoved(ListRemoveEvent event) { - ListPropertyValueModelAdapter.this.itemsRemoved(event); - } - @Override - public void itemsReplaced(ListReplaceEvent event) { - ListPropertyValueModelAdapter.this.itemsReplaced(event); - } - @Override - public void itemsMoved(ListMoveEvent event) { - ListPropertyValueModelAdapter.this.itemsMoved(event); - } - @Override - public void listCleared(ListClearEvent event) { - ListPropertyValueModelAdapter.this.listCleared(event); - } - @Override - public void listChanged(ListChangeEvent event) { - ListPropertyValueModelAdapter.this.listChanged(event); - } - } - - - // ********** listener ********** - - /** - * Start listening to the list holder. - */ - @Override - protected void engageModel_() { - this.listModel.addListChangeListener(ListValueModel.LIST_VALUES, this.listListener); - } - - /** - * Stop listening to the list holder. - */ - @Override - protected void disengageModel_() { - this.listModel.removeListChangeListener(ListValueModel.LIST_VALUES, this.listListener); - } - - - // ********** list change support ********** - - /** - * Items were added to the wrapped list holder; - * propagate the change notification appropriately. - */ - protected void itemsAdded(@SuppressWarnings("unused") ListAddEvent event) { - // by default, simply recalculate the value and fire an event - this.propertyChanged(); - } - - /** - * Items were removed from the wrapped list holder; - * propagate the change notification appropriately. - */ - protected void itemsRemoved(@SuppressWarnings("unused") ListRemoveEvent event) { - // by default, simply recalculate the value and fire an event - this.propertyChanged(); - } - - /** - * Items were replaced in the wrapped list holder; - * propagate the change notification appropriately. - */ - protected void itemsReplaced(@SuppressWarnings("unused") ListReplaceEvent event) { - // by default, simply recalculate the value and fire an event - this.propertyChanged(); - } - - /** - * Items were moved in the wrapped list holder; - * propagate the change notification appropriately. - */ - protected void itemsMoved(@SuppressWarnings("unused") ListMoveEvent event) { - // by default, simply recalculate the value and fire an event - this.propertyChanged(); - } - - /** - * The wrapped list holder was cleared; - * propagate the change notification appropriately. - */ - protected void listCleared(@SuppressWarnings("unused") ListClearEvent event) { - // by default, simply recalculate the value and fire an event - this.propertyChanged(); - } - - /** - * The value of the wrapped list holder has changed; - * propagate the change notification appropriately. - */ - protected void listChanged(@SuppressWarnings("unused") ListChangeEvent event) { - // by default, simply recalculate the value and fire an event - this.propertyChanged(); - } -} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListValueModelTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListValueModelTools.java new file mode 100644 index 0000000000..1e49f0a853 --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListValueModelTools.java @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import org.eclipse.jpt.common.utility.closure.Closure; +import org.eclipse.jpt.common.utility.internal.closure.BooleanClosure; +import org.eclipse.jpt.common.utility.internal.closure.ClosureTools; +import org.eclipse.jpt.common.utility.internal.transformer.TransformerTools; +import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; +import org.eclipse.jpt.common.utility.model.value.ListValueModel; +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.predicate.Predicate; +import org.eclipse.jpt.common.utility.transformer.Transformer; + +/** + * {@link ListValueModel List value model} utility methods. + */ +public final class ListValueModelTools { + + // ********** composite PVMs ********** + + /** + * Construct a composite property value model adapter for the specified + * transformer and property value models. + */ + @SafeVarargs + public static <E, V> PropertyValueModel<V> compositePropertyValueModel(Transformer<? super List<E>, V> transformer, PropertyValueModel<? extends E>... propertyValueModels) { + return compositePropertyValueModel(Arrays.asList(propertyValueModels), transformer); + } + + /** + * Construct a composite property value model adapter for the specified + * property value models and transformer. + */ + public static <E, V> PropertyValueModel<V> compositePropertyValueModel(Collection<? extends PropertyValueModel<? extends E>> propertyValueModels, Transformer<? super List<E>, V> transformer) { + return compositePropertyValueModel(new StaticListValueModel<>(propertyValueModels), transformer); + } + + /** + * Construct a composite property value model adapter for the specified + * list value model and transformer. + * @see PluggablePropertyValueModel + */ + public static <E, V> PropertyValueModel<V> compositePropertyValueModel(ListValueModel<? extends PropertyValueModel<? extends E>> listModel, Transformer<? super List<E>, V> transformer) { + return PropertyValueModelTools.propertyValueModel(new ListCompositePropertyValueModelAdapter.Factory<>(listModel, transformer)); + } + + + // ********** list meta data adapters ********** + + /** + * Construct a property value model adapter for the specified + * list value model. + * If the list is empty, the model's value is <code>null</code>; + * otherwise, it is the first element in the list. + */ + public static <E> PropertyValueModel<E> firstElementPropertyValueModel(ListValueModel<? extends E> listModel) { + return propertyValueModel(listModel, TransformerTools.collectionFirstElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * list value model. + * If the list is empty, the model's value is <code>null</code>; + * otherwise, it is the last element in the list. + */ + public static <E> PropertyValueModel<E> lastElementPropertyValueModel(ListValueModel<? extends E> listModel) { + return propertyValueModel(listModel, TransformerTools.collectionLastElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * list value model. + * If the list contains <em>only</em> a single element, + * the model's value is that element; otherwise, + * the model's value is <code>null</code>. + */ + public static <E> PropertyValueModel<E> singleElementPropertyValueModel(ListValueModel<? extends E> listModel) { + return propertyValueModel(listModel, TransformerTools.collectionSingleElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * list value model that returns whether the list is <em>not</em> empty. + */ + public static PropertyValueModel<Boolean> isNotEmptyPropertyValueModel(ListValueModel<?> listModel) { + return propertyValueModel(listModel, TransformerTools.collectionIsNotEmptyTransformer()); + } + + /** + * Construct a <em>modifiable</em> property value model adapter for the specified + * list value model that returns whether the list is <em>not</em> empty. + * This model can also be used to populate or clear the list value model. + * The specified list mutator is used to convert the list value model: + * it should populate the list value model when passed <code>true</code> + * and clear the list value model when passed <code>false</code>. + */ + public static ModifiablePropertyValueModel<Boolean> isNotEmptyModifiablePropertyValueModel(ListValueModel<?> listModel, BooleanClosure.Adapter listMutator) { + return booleanModifiablePropertyValueModel(listModel, TransformerTools.collectionIsNotEmptyTransformer(), listMutator); + } + + /** + * Construct a property value model adapter for the specified + * list value model that returns whether the list is empty. + */ + public static PropertyValueModel<Boolean> isEmptyPropertyValueModel(ListValueModel<?> listModel) { + return propertyValueModel(listModel, TransformerTools.collectionIsEmptyTransformer()); + } + + /** + * Construct a <em>modifiable</em> property value model adapter for the specified + * list value model that returns whether the list is empty. + * This model can also be used to populate or clear the list value model. + * The specified list mutator is used to convert the list value model: + * it should populate the list value model when passed <code>true</code> + * and clear the list value model when passed <code>false</code>. + */ + public static ModifiablePropertyValueModel<Boolean> isEmptyModifiablePropertyValueModel(ListValueModel<?> listModel, BooleanClosure.Adapter listMutator) { + return booleanModifiablePropertyValueModel(listModel, TransformerTools.collectionIsEmptyTransformer(), listMutator); + } + + /** + * Construct a property value model adapter for the specified + * list value model that returns whether the list contains + * exactly one element. + */ + public static PropertyValueModel<Boolean> containsSingleElementPropertyValueModel(ListValueModel<?> listModel) { + return propertyValueModel(listModel, TransformerTools.collectionContainsSingleElementTransformer()); + } + + /** + * Construct a property value model adapter for the specified + * list value model that returns whether the list's size + * equals the specified size. + */ + public static PropertyValueModel<Boolean> sizeEqualsPropertyValueModel(ListValueModel<?> listModel, int size) { + return propertyValueModel(listModel, TransformerTools.collectionSizeEqualsTransformer(size)); + } + + // ********** PVM adapters ********** + + /** + * Construct a boolean property value model adapter for the specified + * list value model and predicate. The returned model will indicate whether + * the list belongs to the set defined by the predicate. + */ + public static PropertyValueModel<Boolean> booleanPropertyValueModel(ListValueModel<?> listModel, Predicate<? super List<?>> predicate) { + return propertyValueModel(listModel, TransformerTools.adapt(predicate)); + } + + /** + * Construct a property value model adapted to the specified + * list value model and transformer. + * @see PluggablePropertyValueModel + */ + public static <E, V> PropertyValueModel<V> propertyValueModel(ListValueModel<? extends E> listModel, Transformer<? super List<E>, V> transformer) { + return PropertyValueModelTools.propertyValueModel(pluggablePropertyValueModelAdapterFactory(listModel, transformer)); + } + + /** + * Construct a pluggable property value model adapter factory for the specified + * list value model and transformer. + * @see PluggablePropertyValueModel + */ + public static <E, V> PluggablePropertyValueModel.Adapter.Factory<V> pluggablePropertyValueModelAdapterFactory(ListValueModel<? extends E> listModel, Transformer<? super List<E>, V> transformer) { + return new ListPluggablePropertyValueModelAdapter.Factory<>(listModel, transformer); + } + + /** + * Construct a <em>modifiable</em> property value model adapter for the specified + * list value model that returns whether the list belongs to the set defined + * by the specified predicate. + * This model will use the specified list mutator to modify the list value model. + */ + public static ModifiablePropertyValueModel<Boolean> booleanModifiablePropertyValueModel(ListValueModel<?> listModel, Predicate<? super Collection<?>> predicate, BooleanClosure.Adapter listMutator) { + return booleanModifiablePropertyValueModel(listModel, TransformerTools.adapt(predicate), listMutator); + } + + /** + * Construct a <em>modifiable</em> property value model adapted to the specified + * list value model that returns whether the list belongs to the set defined + * by the specified transformer. + * This model will use the specified list mutator to modify the list value model. + */ + public static ModifiablePropertyValueModel<Boolean> booleanModifiablePropertyValueModel(ListValueModel<?> listModel, Transformer<? super Collection<?>, Boolean> transformer, BooleanClosure.Adapter listMutator) { + PluggablePropertyValueModel.Adapter.Factory<Boolean> factory = pluggablePropertyValueModelAdapterFactory(listModel, transformer); + Closure<Boolean> closure = ClosureTools.booleanClosure(listMutator); + return PropertyValueModelTools.pluggableModifiablePropertyValueModel(factory, closure); + } + + + // ********** filtering ********** + + /** + * Construct a collection value model with the specified wrapped list value model and filter. + */ + public static <E> CollectionValueModel<E> filter(ListValueModel<? extends E> listModel, Predicate<E> filter) { + return CollectionValueModelTools.filter(new ListCollectionValueModelAdapter<E>(listModel), filter); + } + + + // ********** suppressed constructor ********** + + /** + * Suppress default constructor, ensuring non-instantiability. + */ + private ListValueModelTools() { + super(); + throw new UnsupportedOperationException(); + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggableModifiablePropertyValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggableModifiablePropertyValueModel.java new file mode 100644 index 0000000000..b9bbf39ca6 --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggableModifiablePropertyValueModel.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; + +/** + * This class provides the infrastructure needed to wrap + * a <em>modifiable</em> model, "lazily" listen to it, and convert + * its change notifications into <em>property</em> value model change + * notifications. + * + * @param <V> the type of the model's derived value + */ +public class PluggableModifiablePropertyValueModel<V> + extends AbstractPluggablePropertyValueModel<V, PluggableModifiablePropertyValueModel.Adapter<V>> + implements ModifiablePropertyValueModel<V> +{ + public PluggableModifiablePropertyValueModel(Adapter.Factory<V> adapterFactory) { + super(adapterFactory); + } + + + // ********** ModifiablePropertyValueModel implementation ********** + + /** + * Forward the new value to the adapter. + * Our value will be updated by notification from the adapter, + * if appropriate. + */ + public void setValue(V value) { + this.adapter.setValue(value); + } + + + // ********** Adapter interfaces ********** + + public interface Adapter<AV> + extends AbstractPluggablePropertyValueModel.Adapter<AV> + { + /** + * Set the adapted model's value, + * based on the specified new value of the property value model. + */ + void setValue(AV value); + + interface Factory<AFV> + extends AbstractPluggablePropertyValueModel.Adapter.Factory<AFV, Adapter<AFV>> + { + // NOP + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggableModifiablePropertyValueModelAdapter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggableModifiablePropertyValueModelAdapter.java new file mode 100644 index 0000000000..409b8991d1 --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggableModifiablePropertyValueModelAdapter.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.internal.model.value; + +import org.eclipse.jpt.common.utility.closure.Closure; +import org.eclipse.jpt.common.utility.internal.ObjectTools; + +/** + * This class adds support for plugging in a closure that can be used to set + * the model's value. + * <p> + * This class is most useful when the adapted model is changed <em>outside</em> + * the property value model; typically by modifying the original + * <em>domain</em> model. + * + * @param <V> the type of the model's derived value + */ +public class PluggableModifiablePropertyValueModelAdapter<V> + implements PluggableModifiablePropertyValueModel.Adapter<V> +{ + /** Read the adapted model with this. */ + private final AbstractPluggablePropertyValueModel.Adapter<V> adapter; + + /** Write the adapted model with this. */ + private final Closure<V> closure; + + + public PluggableModifiablePropertyValueModelAdapter(AbstractPluggablePropertyValueModel.Adapter<V> adapter, Closure<V> closure) { + super(); + if (adapter == null) { + throw new NullPointerException(); + } + this.adapter = adapter; + if (closure == null) { + throw new NullPointerException(); + } + this.closure = closure; + } + + public V getValue() { + return this.adapter.getValue(); + } + + public void setValue(V value) { + this.closure.execute(value); + } + + public void engageModel() { + this.adapter.engageModel(); + } + + public void disengageModel() { + this.adapter.disengageModel(); + } + + + // ********** PluggableModifiablePropertyValueModel.Adapter.Factory ********** + + public static class Factory<V> + implements PluggableModifiablePropertyValueModel.Adapter.Factory<V> + { + /* CU private */ final AbstractPluggablePropertyValueModel.Adapter.Factory<V, ? extends AbstractPluggablePropertyValueModel.Adapter<V>> factory; + /* CU private */ final Closure<V> closure; + + public Factory(AbstractPluggablePropertyValueModel.Adapter.Factory<V, ? extends AbstractPluggablePropertyValueModel.Adapter<V>> factory, Closure<V> closure) { + super(); + if (factory == null) { + throw new NullPointerException(); + } + this.factory = factory; + if (closure == null) { + throw new NullPointerException(); + } + this.closure = closure; + } + + public PluggableModifiablePropertyValueModel.Adapter<V> buildAdapter(AbstractPluggablePropertyValueModel.Adapter.Listener<V> listener) { + return new PluggableModifiablePropertyValueModelAdapter<>(this.factory.buildAdapter(listener), this.closure); + } + + @Override + public String toString() { + return ObjectTools.toString(this); + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggablePropertyValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggablePropertyValueModel.java new file mode 100644 index 0000000000..42f248d7c1 --- /dev/null +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PluggablePropertyValueModel.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2008, 2016 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.common.utility.internal.model.value; + +/** + * This class provides the infrastructure needed to wrap + * a model, "lazily" listen to it, and convert + * its change notifications into <em>property</em> value model change + * notifications. + * + * @param <V> the type of the model's derived value + */ +public class PluggablePropertyValueModel<V> + extends AbstractPluggablePropertyValueModel<V, PluggablePropertyValueModel.Adapter<V>> +{ + public PluggablePropertyValueModel(Adapter.Factory<V> adapterFactory) { + super(adapterFactory); + } + + + // ********** Adapter interfaces ********** + + public interface Adapter<AV> + extends AbstractPluggablePropertyValueModel.Adapter<AV> + { + interface Factory<AFV> + extends AbstractPluggablePropertyValueModel.Adapter.Factory<AFV, Adapter<AFV>> + { + // NOP + } + } +} diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueModelTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PropertyValueModelTools.java index 79c106fe24..7b1835cbe6 100644 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueModelTools.java +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PropertyValueModelTools.java @@ -9,13 +9,39 @@ ******************************************************************************/ package org.eclipse.jpt.common.utility.internal.model.value; +import org.eclipse.jpt.common.utility.closure.Closure; import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; /** * Value Model utility methods. */ -public final class ValueModelTools { +public final class PropertyValueModelTools { + + /** + * Construct a property value model adapter for the specified adapter factory. + * @see PluggablePropertyValueModel + */ + public static <V> PropertyValueModel<V> propertyValueModel(PluggablePropertyValueModel.Adapter.Factory<V> adapterFactory) { + return new PluggablePropertyValueModel<>(adapterFactory); + } + + /** + * Construct a modifiable property value model adapter for the specified adapter factory. + * @see PluggableModifiablePropertyValueModel + */ + public static <V> ModifiablePropertyValueModel<V> modifiablePropertyValueModel(PluggableModifiablePropertyValueModel.Adapter.Factory<V> adapterFactory) { + return new PluggableModifiablePropertyValueModel<>(adapterFactory); + } + + /** + * Construct a <em>modifiable</em> property value model adapter for the specified + * property value model adapter adapter factory and closure. + * The specified closure is invoked when the model's value is set. + */ + public static <V> ModifiablePropertyValueModel<V> pluggableModifiablePropertyValueModel(AbstractPluggablePropertyValueModel.Adapter.Factory<V, ? extends AbstractPluggablePropertyValueModel.Adapter<V>> factory, Closure<V> setValueClosure) { + return new PluggableModifiablePropertyValueModel<>(new PluggableModifiablePropertyValueModelAdapter.Factory<>(factory, setValueClosure)); + } // ********** double PVMs ********** @@ -44,7 +70,7 @@ public final class ValueModelTools { /** * Suppress default constructor, ensuring non-instantiability. */ - private ValueModelTools() { + private PropertyValueModelTools() { super(); throw new UnsupportedOperationException(); } diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleCollectionValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleCollectionValueModel.java index 3842893b13..c7dcb27ea9 100644 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleCollectionValueModel.java +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleCollectionValueModel.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2015 Oracle. All rights reserved. + * Copyright (c) 2007, 2016 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. @@ -62,7 +62,7 @@ public class SimpleCollectionValueModel<E> // ********** CollectionValueModel implementation ********** public Iterator<E> iterator() { - return new LocalIterator<E>(this.collection.iterator()); + return new LocalIterator<>(this.collection.iterator()); } public int size() { diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleListValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleListValueModel.java index da81f0c2de..3c633626b7 100644 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleListValueModel.java +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleListValueModel.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2015 Oracle. All rights reserved. + * Copyright (c) 2007, 2016 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. @@ -65,11 +65,11 @@ public class SimpleListValueModel<E> // ********** ListValueModel implementation ********** public Iterator<E> iterator() { - return new LocalIterator<E>(this.list.iterator()); + return new LocalIterator<>(this.list.iterator()); } public ListIterator<E> listIterator() { - return new LocalListIterator<E>(this.list.listIterator()); + return new LocalListIterator<>(this.list.listIterator()); } public int size() { @@ -186,7 +186,7 @@ public class SimpleListValueModel<E> } public ListIterator<E> listIterator(int index) { - return new LocalListIterator<E>(this.list.listIterator(index)); + return new LocalListIterator<>(this.list.listIterator(index)); } public List<E> subList(int fromIndex, int toIndex) { diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/PropertyValueModel.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/PropertyValueModel.java index c098c884de..c39b5df007 100644 --- a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/PropertyValueModel.java +++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/PropertyValueModel.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2012 Oracle. All rights reserved. + * Copyright (c) 2007, 2016 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. @@ -9,7 +9,9 @@ ******************************************************************************/ package org.eclipse.jpt.common.utility.model.value; +import org.eclipse.jpt.common.utility.internal.ObjectTools; import org.eclipse.jpt.common.utility.model.Model; +import org.eclipse.jpt.common.utility.transformer.Transformer; /** * Interface used to abstract property accessing and @@ -31,4 +33,18 @@ public interface PropertyValueModel<V> */ V getValue(); String VALUE = "value"; //$NON-NLS-1$ + + @SuppressWarnings("rawtypes") + Transformer VALUE_TRANSFORMER = new ValueTransformer(); + class ValueTransformer<V> + implements Transformer<PropertyValueModel<V>, V> + { + public V transform(PropertyValueModel<V> pvm) { + return pvm.getValue(); + } + @Override + public String toString() { + return ObjectTools.singletonToString(this); + } + } } diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CollectionPropertyValueModelAdapterTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CollectionPropertyValueModelAdapterTests.java index bdf7a7e20b..bc9ae272a0 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CollectionPropertyValueModelAdapterTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CollectionPropertyValueModelAdapterTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2015 Oracle. All rights reserved. + * Copyright (c) 2007, 2016 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. @@ -10,13 +10,12 @@ package org.eclipse.jpt.common.utility.tests.internal.model.value; import java.util.Collection; - -import junit.framework.TestCase; - +import org.eclipse.jpt.common.utility.internal.ObjectTools; import org.eclipse.jpt.common.utility.internal.collection.CollectionTools; -import org.eclipse.jpt.common.utility.internal.iterator.IteratorTools; import org.eclipse.jpt.common.utility.internal.model.AbstractModel; -import org.eclipse.jpt.common.utility.internal.model.value.CollectionPropertyValueModelAdapter; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionPluggablePropertyValueModelAdapter; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.PropertyValueModelTools; import org.eclipse.jpt.common.utility.internal.model.value.SimpleCollectionValueModel; import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; import org.eclipse.jpt.common.utility.model.listener.ChangeAdapter; @@ -24,13 +23,16 @@ import org.eclipse.jpt.common.utility.model.listener.ChangeListener; import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; -import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; import org.eclipse.jpt.common.utility.tests.internal.TestTools; +import org.eclipse.jpt.common.utility.transformer.Transformer; +import junit.framework.TestCase; @SuppressWarnings("nls") -public class CollectionPropertyValueModelAdapterTests extends TestCase { - private ModifiablePropertyValueModel<Boolean> adapter; - private SimpleCollectionValueModel<String> wrappedCollectionHolder; +public class CollectionPropertyValueModelAdapterTests + extends TestCase +{ + private PropertyValueModel<Boolean> adapter; + private SimpleCollectionValueModel<String> collectionModel; PropertyChangeEvent event; public CollectionPropertyValueModelAdapterTests(String name) { @@ -40,8 +42,8 @@ public class CollectionPropertyValueModelAdapterTests extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - this.wrappedCollectionHolder = new SimpleCollectionValueModel<String>(); - this.adapter = new LocalAdapter(this.wrappedCollectionHolder, "666"); + this.collectionModel = new SimpleCollectionValueModel<>(); + this.adapter = CollectionValueModelTools.propertyValueModel(this.collectionModel, new LocalTransformer("666")); this.event = null; } @@ -52,11 +54,12 @@ public class CollectionPropertyValueModelAdapterTests extends TestCase { } private boolean booleanValue() { - return this.adapter.getValue().booleanValue(); + Boolean value = this.adapter.getValue(); + return (value != null) && value.booleanValue(); } private Collection<String> wrappedCollection() { - return CollectionTools.hashBag(this.wrappedCollectionHolder.iterator()); + return CollectionTools.hashBag(this.collectionModel.iterator()); } public void testValue() { @@ -66,41 +69,25 @@ public class CollectionPropertyValueModelAdapterTests extends TestCase { assertFalse(this.booleanValue()); assertFalse(this.wrappedCollection().contains("666")); - this.wrappedCollectionHolder.add("111"); + this.collectionModel.add("111"); assertFalse(this.booleanValue()); - this.wrappedCollectionHolder.add("222"); + this.collectionModel.add("222"); assertFalse(this.booleanValue()); - this.wrappedCollectionHolder.add("666"); + this.collectionModel.add("666"); assertTrue(this.booleanValue()); assertTrue(this.wrappedCollection().contains("666")); - this.wrappedCollectionHolder.remove("666"); + this.collectionModel.remove("666"); assertFalse(this.booleanValue()); assertFalse(this.wrappedCollection().contains("666")); - this.wrappedCollectionHolder.add("666"); + this.collectionModel.add("666"); assertTrue(this.booleanValue()); assertTrue(this.wrappedCollection().contains("666")); - this.wrappedCollectionHolder.clear(); - assertFalse(this.booleanValue()); - assertFalse(this.wrappedCollection().contains("666")); - } - - public void testSetValue() { - this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, new PropertyChangeListener() { - public void propertyChanged(PropertyChangeEvent e) {/* OK */} - }); - assertFalse(this.booleanValue()); - assertFalse(this.wrappedCollection().contains("666")); - - this.adapter.setValue(Boolean.TRUE); - assertTrue(this.booleanValue()); - assertTrue(this.wrappedCollection().contains("666")); - - this.adapter.setValue(Boolean.FALSE); + this.collectionModel.clear(); assertFalse(this.booleanValue()); assertFalse(this.wrappedCollection().contains("666")); } @@ -113,22 +100,22 @@ public class CollectionPropertyValueModelAdapterTests extends TestCase { }); assertNull(this.event); - this.wrappedCollectionHolder.add("111"); + this.collectionModel.add("111"); assertNull(this.event); - this.wrappedCollectionHolder.add("222"); + this.collectionModel.add("222"); assertNull(this.event); - this.wrappedCollectionHolder.add("666"); + this.collectionModel.add("666"); this.verifyEvent(false, true); - this.wrappedCollectionHolder.remove("666"); + this.collectionModel.remove("666"); this.verifyEvent(true, false); - this.wrappedCollectionHolder.add("666"); + this.collectionModel.add("666"); this.verifyEvent(false, true); - this.wrappedCollectionHolder.clear(); + this.collectionModel.clear(); this.verifyEvent(true, false); } @@ -144,7 +131,7 @@ public class CollectionPropertyValueModelAdapterTests extends TestCase { public void propertyChanged(PropertyChangeEvent e) {/* OK */} }; this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, listener); - this.wrappedCollectionHolder.add("666"); + this.collectionModel.add("666"); assertTrue(this.booleanValue()); assertTrue(this.wrappedCollection().contains("666")); @@ -159,7 +146,7 @@ public class CollectionPropertyValueModelAdapterTests extends TestCase { public void testHasListeners() { assertFalse(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); - assertFalse(((AbstractModel) this.wrappedCollectionHolder).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + assertFalse(((AbstractModel) this.collectionModel).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); ChangeListener listener = new ChangeAdapter() { @Override @@ -167,73 +154,130 @@ public class CollectionPropertyValueModelAdapterTests extends TestCase { }; this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, listener); assertTrue(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); - assertTrue(((AbstractModel) this.wrappedCollectionHolder).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + assertTrue(((AbstractModel) this.collectionModel).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); this.adapter.removePropertyChangeListener(PropertyValueModel.VALUE, listener); assertFalse(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); - assertFalse(((AbstractModel) this.wrappedCollectionHolder).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + assertFalse(((AbstractModel) this.collectionModel).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); this.adapter.addChangeListener(listener); assertTrue(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); - assertTrue(((AbstractModel) this.wrappedCollectionHolder).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + assertTrue(((AbstractModel) this.collectionModel).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); this.adapter.removeChangeListener(listener); assertFalse(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); - assertFalse(((AbstractModel) this.wrappedCollectionHolder).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + assertFalse(((AbstractModel) this.collectionModel).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + } + + public void testToString1() { + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, new PropertyChangeListener() { + public void propertyChanged(PropertyChangeEvent e) {/* OK */} + }); + assertTrue(this.adapter.toString().endsWith("(false)")); + this.collectionModel.add("666"); + assertTrue(this.adapter.toString().endsWith("(true)")); + } + + public void testToString3() { + CollectionPluggablePropertyValueModelAdapter.Factory<String, Boolean> f = new CollectionPluggablePropertyValueModelAdapter.Factory<>(this.collectionModel, new LocalTransformer("666")); + assertTrue(f.toString().indexOf("Factory") != -1); + } + + public void testToString4() { + PropertyChangeListener listener = new PropertyChangeListener() { + public void propertyChanged(PropertyChangeEvent e) {/* OK */} + }; + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + Object a = ObjectTools.get(this.adapter, "adapter"); + Object l = ObjectTools.get(a, "listener"); + assertTrue(l.toString().indexOf("AdapterListener") != -1); + } + + public void testCtor_NPE1A() { + Object object; + boolean exCaught = false; + try { + object = CollectionValueModelTools.propertyValueModel(null, new LocalTransformer("666")); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE1B() { + Object object; + boolean exCaught = false; + try { + object = CollectionValueModelTools.propertyValueModel(null, new LocalTransformer("666")); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE2A() { + Object object; + boolean exCaught = false; + try { + object = CollectionValueModelTools.propertyValueModel(this.collectionModel, null); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE2B() { + Object object; + boolean exCaught = false; + try { + object = CollectionValueModelTools.propertyValueModel(this.collectionModel, null); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE4() { + Object object; + boolean exCaught = false; + try { + object = PropertyValueModelTools.propertyValueModel(null); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); } // ********** member class ********** /** - * the value is true if the wrapped collection contains the specified item, - * otherwise the value is false + * Transform the collection to <code>true</code> if it contains the specified item, + * otherwise transform it to <code>false</code>. */ - static class LocalAdapter - extends CollectionPropertyValueModelAdapter<Boolean, String> - implements ModifiablePropertyValueModel<Boolean> + static class LocalTransformer + implements Transformer<Collection<String>, Boolean> { - private String item; + private final String item; - LocalAdapter(CollectionValueModel<String> collectionHolder, String item) { - super(collectionHolder); + LocalTransformer(String item) { + super(); this.item = item; } - // ********** CollectionPropertyValueModelAdapter implementation ********** - /** - * always return a Boolean - */ - @Override - public Boolean getValue() { - Boolean result = super.getValue(); - return (result == null) ? Boolean.FALSE : result; - } - @SuppressWarnings("unchecked") - public void setValue(Boolean value) { - if (this.booleanValue()) { - if ( ! this.booleanValueOf(value)) { - // the value is changing from true to false - ((SimpleCollectionValueModel<String>) this.collectionModel).remove(this.item); - } - } else { - if (this.booleanValueOf(value)) { - // the value is changing from false to true - ((SimpleCollectionValueModel<String>) this.collectionModel).add(this.item); - } - } - } - @Override - protected Boolean buildValue() { - return Boolean.valueOf(IteratorTools.contains(this.collectionModel.iterator(), this.item)); + public Boolean transform(Collection<String> collection) { + return Boolean.valueOf(collection.contains(this.item)); } - // ********** internal methods ********** - private boolean booleanValue() { - return this.booleanValueOf(this.value); - } - private boolean booleanValueOf(Object b) { - return (b == null) ? false : ((Boolean) b).booleanValue(); + @Override + public String toString() { + return ObjectTools.toString(this, this.item); } } } diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CollectionValueModelToolsTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CollectionValueModelToolsTests.java new file mode 100644 index 0000000000..e7f0f726a1 --- /dev/null +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CollectionValueModelToolsTests.java @@ -0,0 +1,438 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.tests.internal.model.value; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import org.eclipse.jpt.common.utility.internal.ClassTools; +import org.eclipse.jpt.common.utility.internal.closure.BooleanClosure; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.SimpleCollectionValueModel; +import org.eclipse.jpt.common.utility.internal.predicate.PredicateTools; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter; +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import junit.framework.TestCase; + +@SuppressWarnings("nls") +public class CollectionValueModelToolsTests + extends TestCase +{ + public CollectionValueModelToolsTests(String name) { + super(name); + } + + public void testFirstElementPVMAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<String> pvm = CollectionValueModelTools.firstElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertNull(pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertEquals("foo", pvm.getValue()); + assertEquals("foo", listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().equals("foo") || pvm.getValue().equals("bar")); + + listener.event = null; + cvm.remove("foo"); + assertEquals("bar", pvm.getValue()); + // unpredictable + // assertEquals("bar", listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + } + + public void testLastElementPVMAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<String> pvm = CollectionValueModelTools.lastElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertNull(pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertEquals("foo", pvm.getValue()); + assertEquals("foo", listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().equals("foo") || pvm.getValue().equals("bar")); + + listener.event = null; + cvm.remove("foo"); + assertEquals("bar", pvm.getValue()); + // unpredictable + // assertEquals("bar", listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + } + + public void testSingleElementPVMAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<String> pvm = CollectionValueModelTools.singleElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertNull(pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertEquals("foo", pvm.getValue()); + assertEquals("foo", listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + + listener.event = null; + cvm.remove("foo"); + assertEquals("bar", pvm.getValue()); + assertEquals("bar", listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + } + + public void testIsNotEmptyPropertyValueModelAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<Boolean> pvm = CollectionValueModelTools.isNotEmptyPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + } + + public void testIsNotEmptyModifiablePropertyValueModelAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + BooleanClosure.Adapter adapter = new NotEmptyBooleanClosureAdapter(cvm); + ModifiablePropertyValueModel<Boolean> pvm = CollectionValueModelTools.isNotEmptyModifiablePropertyValueModel(cvm, adapter); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + pvm.setValue(Boolean.FALSE); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + assertEquals(0, cvm.size()); + + listener.event = null; + pvm.setValue(Boolean.TRUE); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + assertEquals(2, cvm.size()); + assertTrue(cvm.contains("baz")); + assertTrue(cvm.contains("xxx")); + + listener.event = null; + cvm.remove("baz"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("xxx"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + } + + public static class NotEmptyBooleanClosureAdapter + implements BooleanClosure.Adapter + { + final SimpleCollectionValueModel<String> cvm; + public NotEmptyBooleanClosureAdapter(SimpleCollectionValueModel<String> cvm) { + this.cvm = cvm; + } + public void execute(boolean argument) { + if (argument) { + ArrayList<String> list = new ArrayList<>(); + list.add("baz"); + list.add("xxx"); + this.cvm.setValues(list); + } else { + this.cvm.clear(); + } + } + } + + public void testIsEmptyPropertyValueModelAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<Boolean> pvm = CollectionValueModelTools.isEmptyPropertyValueModel(cvm); + this.verifyIsEmptyPropertyValueModelAdapter(cvm, pvm); + } + + private void verifyIsEmptyPropertyValueModelAdapter(SimpleCollectionValueModel<String> cvm, PropertyValueModel<Boolean> pvm) { + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + } + + public void testBooleanPVM() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<Boolean> pvm = CollectionValueModelTools.booleanPropertyValueModel(cvm, PredicateTools.collectionIsEmptyPredicate()); + this.verifyIsEmptyPropertyValueModelAdapter(cvm, pvm); + } + + public void testIsEmptyModifiablePropertyValueModelAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + BooleanClosure.Adapter adapter = new EmptyBooleanClosureAdapter(cvm); + ModifiablePropertyValueModel<Boolean> pvm = CollectionValueModelTools.isEmptyModifiablePropertyValueModel(cvm, adapter); + this.verifyIsEmptyModifiablePropertyValueModelAdapter(cvm, pvm); + } + + public void testBooleanModifiablePVM() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + BooleanClosure.Adapter adapter = new EmptyBooleanClosureAdapter(cvm); + ModifiablePropertyValueModel<Boolean> pvm = CollectionValueModelTools.booleanModifiablePropertyValueModel(cvm, PredicateTools.collectionIsEmptyPredicate(), adapter); + this.verifyIsEmptyPropertyValueModelAdapter(cvm, pvm); + } + + private void verifyIsEmptyModifiablePropertyValueModelAdapter(SimpleCollectionValueModel<String> cvm, ModifiablePropertyValueModel<Boolean> pvm) { + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + pvm.setValue(Boolean.TRUE); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + assertEquals(0, cvm.size()); + + listener.event = null; + pvm.setValue(Boolean.FALSE); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + assertEquals(2, cvm.size()); + assertTrue(cvm.contains("baz")); + assertTrue(cvm.contains("xxx")); + + listener.event = null; + cvm.remove("baz"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("xxx"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + } + + public static class EmptyBooleanClosureAdapter + implements BooleanClosure.Adapter + { + final SimpleCollectionValueModel<String> cvm; + public EmptyBooleanClosureAdapter(SimpleCollectionValueModel<String> cvm) { + this.cvm = cvm; + } + public void execute(boolean argument) { + if (argument) { + this.cvm.clear(); + } else { + ArrayList<String> list = new ArrayList<>(); + list.add("baz"); + list.add("xxx"); + this.cvm.setValues(list); + } + } + } + + public void testContainsSingleElementPropertyValueModelAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<Boolean> pvm = CollectionValueModelTools.containsSingleElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + } + + public void testSizeEqualsPropertyValueModelAdapter() { + SimpleCollectionValueModel<String> cvm = new SimpleCollectionValueModel<>(); + PropertyValueModel<Boolean> pvm = CollectionValueModelTools.sizeEqualsPropertyValueModel(cvm, 2); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("baz"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("baz"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + } + + public static class LocalPropertyChangeListener + extends PropertyChangeAdapter + { + public PropertyChangeEvent event; + public LocalPropertyChangeListener() { + super(); + } + @Override + public void propertyChanged(PropertyChangeEvent e) { + this.event = e; + } + } + + public void testConstructor() { + boolean exCaught = false; + try { + Object o = ClassTools.newInstance(CollectionValueModelTools.class); + fail("bogus: " + o); + } catch (RuntimeException ex) { + if (ex.getCause() instanceof InvocationTargetException) { + if (ex.getCause().getCause() instanceof UnsupportedOperationException) { + exCaught = true; + } + } + } + assertTrue(exCaught); + } +} diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositeBooleanPropertyValueModelTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositeAndBooleanPropertyValueModelTests.java index 29201ce800..a6de1c427a 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositeBooleanPropertyValueModelTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositeAndBooleanPropertyValueModelTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2010, 2015 Oracle. All rights reserved. + * Copyright (c) 2010, 2016 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. @@ -10,22 +10,23 @@ package org.eclipse.jpt.common.utility.tests.internal.model.value; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; - -import junit.framework.TestCase; - -import org.eclipse.jpt.common.utility.internal.model.value.CompositeBooleanPropertyValueModel; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; import org.eclipse.jpt.common.utility.internal.model.value.SimpleCollectionValueModel; import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; import org.eclipse.jpt.common.utility.model.listener.ChangeAdapter; import org.eclipse.jpt.common.utility.model.listener.ChangeListener; import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; -import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; import org.eclipse.jpt.common.utility.tests.internal.TestTools; +import junit.framework.TestCase; -public class CompositeBooleanPropertyValueModelTests extends TestCase { +public class CompositeAndBooleanPropertyValueModelTests + extends TestCase +{ private SimplePropertyValueModel<Boolean> pvm1; private ModifiablePropertyValueModel<Boolean> pvm2; private ModifiablePropertyValueModel<Boolean> pvm3; @@ -36,29 +37,29 @@ public class CompositeBooleanPropertyValueModelTests extends TestCase { PropertyChangeEvent event; - public CompositeBooleanPropertyValueModelTests(String name) { + public CompositeAndBooleanPropertyValueModelTests(String name) { super(name); } @Override protected void setUp() throws Exception { super.setUp(); - this.pvm1 = new SimplePropertyValueModel<Boolean>(Boolean.TRUE); - this.pvm2 = new SimplePropertyValueModel<Boolean>(Boolean.TRUE); - this.pvm3 = new SimplePropertyValueModel<Boolean>(Boolean.TRUE); - this.pvm4 = new SimplePropertyValueModel<Boolean>(Boolean.TRUE); - this.collection = new ArrayList<ModifiablePropertyValueModel<Boolean>>(); + this.pvm1 = new SimplePropertyValueModel<>(Boolean.TRUE); + this.pvm2 = new SimplePropertyValueModel<>(Boolean.TRUE); + this.pvm3 = new SimplePropertyValueModel<>(Boolean.TRUE); + this.pvm4 = new SimplePropertyValueModel<>(Boolean.TRUE); + this.collection = new ArrayList<>(); this.collection.add(this.pvm1); this.collection.add(this.pvm2); this.collection.add(this.pvm3); this.collection.add(this.pvm4); - this.cvm = new SimpleCollectionValueModel<ModifiablePropertyValueModel<Boolean>>(this.collection); + this.cvm = new SimpleCollectionValueModel<>(this.collection); this.compositePVM = this.buildCompositePVM(this.cvm); } private PropertyValueModel<Boolean> buildCompositePVM(CollectionValueModel<ModifiablePropertyValueModel<Boolean>> pvms) { - return CompositeBooleanPropertyValueModel.and(pvms); + return CollectionValueModelTools.and(pvms); } @Override @@ -67,7 +68,23 @@ public class CompositeBooleanPropertyValueModelTests extends TestCase { super.tearDown(); } - public void testGetValue() { + public void testGetValue1() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertTrue(this.compositePVM.getValue().booleanValue()); + } + + public void testGetValue2() { + this.compositePVM = CollectionValueModelTools.and(this.pvm1, this.pvm2, this.pvm3, this.pvm4); + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertTrue(this.compositePVM.getValue().booleanValue()); + } + + public void testGetValue3() { + this.compositePVM = CollectionValueModelTools.and(Arrays.asList(this.pvm1, this.pvm2, this.pvm3, this.pvm4)); assertNull(this.compositePVM.getValue()); ChangeListener listener = this.buildListener(); this.compositePVM.addChangeListener(listener); @@ -136,7 +153,7 @@ public class CompositeBooleanPropertyValueModelTests extends TestCase { private void verifyCollectionChange() { this.event = null; - ModifiablePropertyValueModel<Boolean> pvm = new SimplePropertyValueModel<Boolean>(Boolean.FALSE); + ModifiablePropertyValueModel<Boolean> pvm = new SimplePropertyValueModel<>(Boolean.FALSE); this.cvm.add(pvm); this.verifyEvent(true, false); @@ -145,15 +162,23 @@ public class CompositeBooleanPropertyValueModelTests extends TestCase { this.verifyEvent(false, true); this.event = null; + this.cvm.add(pvm); + this.verifyEvent(true, false); + + this.event = null; this.cvm.clear(); - this.verifyEvent(Boolean.TRUE, null); + this.verifyEvent(false, true); - Collection<ModifiablePropertyValueModel<Boolean>> c2 = new ArrayList<ModifiablePropertyValueModel<Boolean>>(); + this.event = null; + this.cvm.add(pvm); + this.verifyEvent(true, false); + + Collection<ModifiablePropertyValueModel<Boolean>> c2 = new ArrayList<>(); c2.add(this.pvm1); c2.add(this.pvm2); this.event = null; this.cvm.setValues(c2); - this.verifyEvent(null, Boolean.TRUE); + this.verifyEvent(false, true); } public void testLazyListening1() { @@ -182,7 +207,7 @@ public class CompositeBooleanPropertyValueModelTests extends TestCase { return new ChangeAdapter() { @Override public void propertyChanged(PropertyChangeEvent e) { - CompositeBooleanPropertyValueModelTests.this.event = e; + CompositeAndBooleanPropertyValueModelTests.this.event = e; } }; } @@ -192,6 +217,7 @@ public class CompositeBooleanPropertyValueModelTests extends TestCase { } private void verifyEvent(Boolean oldValue, Boolean newValue) { + assertNotNull(this.event); assertEquals(this.compositePVM, this.event.getSource()); assertEquals(PropertyValueModel.VALUE, this.event.getPropertyName()); assertEquals(oldValue, this.event.getOldValue()); diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositeOrBooleanPropertyValueModelTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositeOrBooleanPropertyValueModelTests.java new file mode 100644 index 0000000000..a85722c639 --- /dev/null +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositeOrBooleanPropertyValueModelTests.java @@ -0,0 +1,227 @@ +/******************************************************************************* + * Copyright (c) 2010, 2016 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.common.utility.tests.internal.model.value; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.SimpleCollectionValueModel; +import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.ChangeAdapter; +import org.eclipse.jpt.common.utility.model.listener.ChangeListener; +import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.tests.internal.TestTools; +import junit.framework.TestCase; + +public class CompositeOrBooleanPropertyValueModelTests + extends TestCase +{ + private SimplePropertyValueModel<Boolean> pvm1; + private ModifiablePropertyValueModel<Boolean> pvm2; + private ModifiablePropertyValueModel<Boolean> pvm3; + private ModifiablePropertyValueModel<Boolean> pvm4; + private Collection<ModifiablePropertyValueModel<Boolean>> collection; + private SimpleCollectionValueModel<ModifiablePropertyValueModel<Boolean>> cvm; + private PropertyValueModel<Boolean> compositePVM; + PropertyChangeEvent event; + + + public CompositeOrBooleanPropertyValueModelTests(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + this.pvm1 = new SimplePropertyValueModel<>(Boolean.FALSE); + this.pvm2 = new SimplePropertyValueModel<>(Boolean.FALSE); + this.pvm3 = new SimplePropertyValueModel<>(Boolean.FALSE); + this.pvm4 = new SimplePropertyValueModel<>(Boolean.FALSE); + this.collection = new ArrayList<>(); + this.collection.add(this.pvm1); + this.collection.add(this.pvm2); + this.collection.add(this.pvm3); + this.collection.add(this.pvm4); + this.cvm = new SimpleCollectionValueModel<>(this.collection); + + this.compositePVM = this.buildCompositePVM(this.cvm); + } + + private PropertyValueModel<Boolean> buildCompositePVM(CollectionValueModel<ModifiablePropertyValueModel<Boolean>> pvms) { + return CollectionValueModelTools.or(pvms); + } + + @Override + protected void tearDown() throws Exception { + TestTools.clear(this); + super.tearDown(); + } + + public void testGetValue1() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertFalse(this.compositePVM.getValue().booleanValue()); + } + + public void testGetValue2() { + this.compositePVM = CollectionValueModelTools.or(this.pvm1, this.pvm2, this.pvm3, this.pvm4); + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertFalse(this.compositePVM.getValue().booleanValue()); + } + + public void testGetValue3() { + this.compositePVM = CollectionValueModelTools.or(Arrays.asList(this.pvm1, this.pvm2, this.pvm3, this.pvm4)); + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertFalse(this.compositePVM.getValue().booleanValue()); + } + + public void testValueAndListeners1() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertFalse(this.compositePVM.getValue().booleanValue()); + this.compositePVM.removeChangeListener(listener); + assertNull(this.compositePVM.getValue()); + } + + public void testValueAndListeners2() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + assertFalse(this.compositePVM.getValue().booleanValue()); + this.compositePVM.removePropertyChangeListener(PropertyValueModel.VALUE, listener); + assertNull(this.compositePVM.getValue()); + } + + public void testPropertyChange1() { + this.compositePVM.addChangeListener(this.buildListener()); + this.verifyPropertyChange(); + } + + public void testPropertyChange2() { + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, this.buildListener()); + this.verifyPropertyChange(); + } + + private void verifyPropertyChange() { + this.event = null; + this.pvm1.setValue(Boolean.TRUE); + this.verifyEvent(false, true); + + this.event = null; + this.pvm2.setValue(Boolean.TRUE); + assertNull(this.event); // no change + + this.event = null; + this.pvm2.setValue(Boolean.FALSE); + assertNull(this.event); // no change + + this.event = null; + this.pvm1.setValue(Boolean.FALSE); + this.verifyEvent(true, false); + + this.event = null; + this.pvm4.setValue(Boolean.TRUE); + this.verifyEvent(false, true); + } + + public void testCollectionChange1() { + this.compositePVM.addChangeListener(this.buildListener()); + this.verifyCollectionChange(); + } + + public void testCollectionChange2() { + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, this.buildListener()); + this.verifyCollectionChange(); + } + + private void verifyCollectionChange() { + this.event = null; + ModifiablePropertyValueModel<Boolean> pvm = new SimplePropertyValueModel<>(Boolean.TRUE); + this.cvm.add(pvm); + this.verifyEvent(false, true); + + this.event = null; + this.cvm.remove(pvm); + this.verifyEvent(true, false); + + this.event = null; + this.cvm.add(pvm); + this.verifyEvent(false, true); + + this.event = null; + this.cvm.clear(); + this.verifyEvent(true, false); + + this.event = null; + this.cvm.add(pvm); + this.verifyEvent(false, true); + + Collection<ModifiablePropertyValueModel<Boolean>> c2 = new ArrayList<>(); + c2.add(this.pvm1); + c2.add(this.pvm2); + this.event = null; + this.cvm.setValues(c2); + this.verifyEvent(true, false); + } + + public void testLazyListening1() { + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + ChangeListener listener = this.buildListener(); + + this.compositePVM.addChangeListener(listener); + assertTrue(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + + this.compositePVM.removeChangeListener(listener); + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + } + + public void testLazyListening2() { + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + ChangeListener listener = this.buildListener(); + + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + assertTrue(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + + this.compositePVM.removePropertyChangeListener(PropertyValueModel.VALUE, listener); + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + } + + private ChangeListener buildListener() { + return new ChangeAdapter() { + @Override + public void propertyChanged(PropertyChangeEvent e) { + CompositeOrBooleanPropertyValueModelTests.this.event = e; + } + }; + } + + private void verifyEvent(boolean oldValue, boolean newValue) { + this.verifyEvent(Boolean.valueOf(oldValue), Boolean.valueOf(newValue)); + } + + private void verifyEvent(Boolean oldValue, Boolean newValue) { + assertNotNull(this.event); + assertEquals(this.compositePVM, this.event.getSource()); + assertEquals(PropertyValueModel.VALUE, this.event.getPropertyName()); + assertEquals(oldValue, this.event.getOldValue()); + assertEquals(newValue, this.event.getNewValue()); + } + +} diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositePropertyValueModelTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositePropertyValueModelTests.java index 56acea9e90..47f632d08f 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositePropertyValueModelTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/CompositePropertyValueModelTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2015 Oracle. All rights reserved. + * Copyright (c) 2009, 2016 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. @@ -11,27 +11,32 @@ package org.eclipse.jpt.common.utility.tests.internal.model.value; import java.util.ArrayList; import java.util.Collection; - -import junit.framework.TestCase; - -import org.eclipse.jpt.common.utility.internal.model.value.CompositePropertyValueModel; +import org.eclipse.jpt.common.utility.internal.collection.CollectionTools; +import org.eclipse.jpt.common.utility.internal.exception.RuntimeExceptionHandler; +import org.eclipse.jpt.common.utility.internal.model.ChangeSupport; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; import org.eclipse.jpt.common.utility.internal.model.value.SimpleCollectionValueModel; import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; +import org.eclipse.jpt.common.utility.internal.transformer.TransformerAdapter; import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; import org.eclipse.jpt.common.utility.model.listener.ChangeAdapter; import org.eclipse.jpt.common.utility.model.listener.ChangeListener; import org.eclipse.jpt.common.utility.model.value.CollectionValueModel; -import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; import org.eclipse.jpt.common.utility.tests.internal.TestTools; +import junit.framework.TestCase; -public class CompositePropertyValueModelTests extends TestCase { +@SuppressWarnings("nls") +public class CompositePropertyValueModelTests + extends TestCase +{ private SimplePropertyValueModel<Integer> pvm1; private ModifiablePropertyValueModel<Integer> pvm2; private ModifiablePropertyValueModel<Integer> pvm3; private ModifiablePropertyValueModel<Integer> pvm4; - private Collection<ModifiablePropertyValueModel<Integer>> collection; - private SimpleCollectionValueModel<ModifiablePropertyValueModel<Integer>> cvm; + private Collection<PropertyValueModel<Integer>> collection; + private SimpleCollectionValueModel<PropertyValueModel<Integer>> cvm; private PropertyValueModel<Integer> compositePVM; PropertyChangeEvent event; @@ -43,31 +48,49 @@ public class CompositePropertyValueModelTests extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - this.pvm1 = new SimplePropertyValueModel<Integer>(Integer.valueOf(1)); - this.pvm2 = new SimplePropertyValueModel<Integer>(Integer.valueOf(2)); - this.pvm3 = new SimplePropertyValueModel<Integer>(Integer.valueOf(3)); - this.pvm4 = new SimplePropertyValueModel<Integer>(Integer.valueOf(4)); - this.collection = new ArrayList<ModifiablePropertyValueModel<Integer>>(); + this.pvm1 = new SimplePropertyValueModel<>(Integer.valueOf(1)); + this.pvm2 = new SimplePropertyValueModel<>(Integer.valueOf(2)); + this.pvm3 = new SimplePropertyValueModel<>(Integer.valueOf(3)); + this.pvm4 = new SimplePropertyValueModel<>(Integer.valueOf(4)); + this.collection = new ArrayList<>(); this.collection.add(this.pvm1); this.collection.add(this.pvm2); this.collection.add(this.pvm3); this.collection.add(this.pvm4); - this.cvm = new SimpleCollectionValueModel<ModifiablePropertyValueModel<Integer>>(this.collection); + this.cvm = new LocalSimpleCollectionValueModel<>(this.collection); this.compositePVM = this.buildCompositePVM(this.cvm); } - private <T extends PropertyValueModel<Integer>> PropertyValueModel<Integer> buildCompositePVM(CollectionValueModel<T> pvms) { - return new CompositePropertyValueModel<Integer, Integer>(pvms) { - @Override - protected Integer buildValue() { - int sum = 0; - for (PropertyValueModel<? extends Integer> each : this.collectionModel) { - sum += each.getValue().intValue(); + public static class LocalSimpleCollectionValueModel<E> + extends SimpleCollectionValueModel<E> + { + public LocalSimpleCollectionValueModel(Collection<E> collection) { + super(collection); + } + @Override + protected ChangeSupport buildChangeSupport() { + return new ChangeSupport(this, RuntimeExceptionHandler.instance()); + } + } + + private PropertyValueModel<Integer> buildCompositePVM(CollectionValueModel<PropertyValueModel<Integer>> pvms) { + return CollectionValueModelTools.compositePropertyValueModel(pvms, new LocalTransformer()); + } + + public static class LocalTransformer + extends TransformerAdapter<Collection<Integer>, Integer> + { + @Override + public Integer transform(Collection<Integer> integers) { + int sum = 0; + for (Integer integer : integers) { + if (integer != null) { + sum += integer.intValue(); } - return Integer.valueOf(sum); } - }; + return Integer.valueOf(sum); + } } @Override @@ -76,7 +99,25 @@ public class CompositePropertyValueModelTests extends TestCase { super.tearDown(); } - public void testGetValue() { + public void testGetValue1() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertEquals(10, this.compositePVM.getValue().intValue()); + } + + public void testGetValue2() { + ArrayList<PropertyValueModel<Integer>> list = new ArrayList<>(); + CollectionTools.addAll(list, this.cvm); + this.compositePVM = CollectionValueModelTools.compositePropertyValueModel(list, new LocalTransformer()); + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertEquals(10, this.compositePVM.getValue().intValue()); + } + + public void testGetValue3() { + this.compositePVM = CollectionValueModelTools.compositePropertyValueModel(new LocalTransformer(), this.pvm1, this.pvm2, this.pvm3, this.pvm4); assertNull(this.compositePVM.getValue()); ChangeListener listener = this.buildListener(); this.compositePVM.addChangeListener(listener); @@ -133,7 +174,7 @@ public class CompositePropertyValueModelTests extends TestCase { private void verifyCollectionChange() { this.event = null; - ModifiablePropertyValueModel<Integer> pvm = new SimplePropertyValueModel<Integer>(Integer.valueOf(77)); + ModifiablePropertyValueModel<Integer> pvm = new SimplePropertyValueModel<>(Integer.valueOf(77)); this.cvm.add(pvm); this.verifyEvent(10, 87); @@ -145,7 +186,7 @@ public class CompositePropertyValueModelTests extends TestCase { this.cvm.clear(); this.verifyEvent(10, 0); - Collection<ModifiablePropertyValueModel<Integer>> c2 = new ArrayList<ModifiablePropertyValueModel<Integer>>(); + Collection<PropertyValueModel<Integer>> c2 = new ArrayList<>(); c2.add(this.pvm1); c2.add(this.pvm2); this.event = null; @@ -175,6 +216,54 @@ public class CompositePropertyValueModelTests extends TestCase { assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); } + public void testCtor_NPE1() { + boolean exCaught = false; + try { + this.compositePVM = this.buildCompositePVM(null); + fail("bogus: " + this.compositePVM); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE2() { + boolean exCaught = false; + try { + this.compositePVM = CollectionValueModelTools.compositePropertyValueModel((CollectionValueModel<PropertyValueModel<Integer>>) this.cvm, (TransformerAdapter<Collection<Integer>, Integer>) null); + fail("bogus: " + this.compositePVM); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testNullPVM() { + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + boolean exCaught = false; + try { + this.cvm.add(null); + fail("bogus: " + this.cvm); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testDuplicatePVM() { + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + boolean exCaught = false; + try { + this.cvm.add(this.pvm1); + fail("bogus: " + this.cvm); + } catch (IllegalStateException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + private ChangeListener buildListener() { return new ChangeAdapter() { @Override @@ -185,6 +274,7 @@ public class CompositePropertyValueModelTests extends TestCase { } private void verifyEvent(int oldValue, int newValue) { + assertNotNull(this.event); assertEquals(this.compositePVM, this.event.getSource()); assertEquals(PropertyValueModel.VALUE, this.event.getPropertyName()); assertEquals(Integer.valueOf(oldValue), this.event.getOldValue()); diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoubleModifiablePropertyValueModelTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoubleModifiablePropertyValueModelTests.java index 369d19e5f5..af4dc8ab43 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoubleModifiablePropertyValueModelTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoubleModifiablePropertyValueModelTests.java @@ -10,7 +10,7 @@ package org.eclipse.jpt.common.utility.tests.internal.model.value; import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; -import org.eclipse.jpt.common.utility.internal.model.value.ValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.PropertyValueModelTools; import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; @@ -24,7 +24,7 @@ public class DoubleModifiablePropertyValueModelTests @Override protected PropertyValueModel<String> buildDoubleModel(ModifiablePropertyValueModel<ModifiablePropertyValueModel<String>> modelModel) { - return ValueModelTools.wrapModifiable(modelModel); + return PropertyValueModelTools.wrapModifiable(modelModel); } protected ModifiablePropertyValueModel<String> getDoubleModel() { @@ -44,7 +44,13 @@ public class DoubleModifiablePropertyValueModelTests this.stringModelModel.setValue(null); assertNull(this.doubleModel.getValue()); - this.getDoubleModel().setValue("TTT"); // NOP? + boolean exCaught = false; + try { + this.getDoubleModel().setValue("TTT"); // unsupported? + } catch (IllegalStateException ex) { + exCaught = true; + } + assertTrue(exCaught); assertEquals("bar", this.stringModel.getValue()); assertNull(this.doubleModel.getValue()); } diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoublePropertyValueModelTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoublePropertyValueModelTests.java index fef14c236c..d6a5fe85e9 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoublePropertyValueModelTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/DoublePropertyValueModelTests.java @@ -11,7 +11,7 @@ package org.eclipse.jpt.common.utility.tests.internal.model.value; import org.eclipse.jpt.common.utility.internal.model.AbstractModel; import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; -import org.eclipse.jpt.common.utility.internal.model.value.ValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.PropertyValueModelTools; import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; import org.eclipse.jpt.common.utility.model.listener.ChangeAdapter; import org.eclipse.jpt.common.utility.model.listener.ChangeListener; @@ -55,7 +55,7 @@ public class DoublePropertyValueModelTests } protected PropertyValueModel<String> buildDoubleModel(ModifiablePropertyValueModel<ModifiablePropertyValueModel<String>> modelModel) { - return ValueModelTools.wrap(modelModel); + return PropertyValueModelTools.wrap(modelModel); } @Override diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/FilteringCollectionValueModelTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/FilteringCollectionValueModelTests.java index 92362adabc..011cf3f978 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/FilteringCollectionValueModelTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/FilteringCollectionValueModelTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2013 Oracle. All rights reserved. + * Copyright (c) 2007, 2016 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. @@ -15,6 +15,7 @@ import java.util.Vector; import junit.framework.TestCase; import org.eclipse.jpt.common.utility.internal.collection.CollectionTools; import org.eclipse.jpt.common.utility.internal.model.AbstractModel; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; import org.eclipse.jpt.common.utility.internal.model.value.FilteringCollectionValueModel; import org.eclipse.jpt.common.utility.internal.model.value.SimpleCollectionValueModel; import org.eclipse.jpt.common.utility.internal.predicate.PredicateAdapter; @@ -31,13 +32,13 @@ import org.eclipse.jpt.common.utility.tests.internal.TestTools; @SuppressWarnings("nls") public class FilteringCollectionValueModelTests extends TestCase { - private SimpleCollectionValueModel<String> collectionHolder; + private SimpleCollectionValueModel<String> simpleCVM; CollectionAddEvent addEvent; CollectionRemoveEvent removeEvent; CollectionClearEvent collectionClearedEvent; CollectionChangeEvent collectionChangedEvent; - private CollectionValueModel<String> filteredCollectionHolder; + private CollectionValueModel<String> filteredCVM; CollectionAddEvent filteredAddEvent; CollectionRemoveEvent filteredRemoveEvent; CollectionClearEvent filteredCollectionClearedEvent; @@ -50,12 +51,12 @@ public class FilteringCollectionValueModelTests extends TestCase { @Override protected void setUp() throws Exception { super.setUp(); - this.collectionHolder = new SimpleCollectionValueModel<String>(buildCollection()); - this.filteredCollectionHolder = new FilteringCollectionValueModel<String>(this.collectionHolder, this.buildFilter()); + this.simpleCVM = new SimpleCollectionValueModel<>(buildCollection()); + this.filteredCVM = CollectionValueModelTools.filter(this.simpleCVM, this.buildFilter()); } private Collection<String> buildCollection() { - Collection<String> collection = new Vector<String>(); + Collection<String> collection = new Vector<>(); collection.add("foo"); return collection; } @@ -81,72 +82,72 @@ public class FilteringCollectionValueModelTests extends TestCase { public void testIterator() { // add a listener to "activate" the wrapper - this.filteredCollectionHolder.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildFilteredListener()); + this.filteredCVM.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildFilteredListener()); - assertEquals("foo", this.collectionHolder.iterator().next()); - assertFalse(this.filteredCollectionHolder.iterator().hasNext()); + assertEquals("foo", this.simpleCVM.iterator().next()); + assertFalse(this.filteredCVM.iterator().hasNext()); - this.collectionHolder.add("bar"); - Iterator<String> collectionHolderValue = this.collectionHolder.iterator(); + this.simpleCVM.add("bar"); + Iterator<String> collectionHolderValue = this.simpleCVM.iterator(); assertEquals("foo", collectionHolderValue.next()); assertEquals("bar", collectionHolderValue.next()); - assertTrue(this.filteredCollectionHolder.iterator().hasNext()); - assertEquals("bar", this.filteredCollectionHolder.iterator().next()); + assertTrue(this.filteredCVM.iterator().hasNext()); + assertEquals("bar", this.filteredCVM.iterator().next()); - this.collectionHolder.remove("bar"); - assertEquals("foo", this.collectionHolder.iterator().next()); - assertFalse(this.filteredCollectionHolder.iterator().hasNext()); + this.simpleCVM.remove("bar"); + assertEquals("foo", this.simpleCVM.iterator().next()); + assertFalse(this.filteredCVM.iterator().hasNext()); - this.collectionHolder.remove("foo"); - assertFalse(this.collectionHolder.iterator().hasNext()); - assertFalse(this.filteredCollectionHolder.iterator().hasNext()); + this.simpleCVM.remove("foo"); + assertFalse(this.simpleCVM.iterator().hasNext()); + assertFalse(this.filteredCVM.iterator().hasNext()); - this.collectionHolder.add("foo"); - assertEquals("foo", this.collectionHolder.iterator().next()); - assertFalse(this.filteredCollectionHolder.iterator().hasNext()); + this.simpleCVM.add("foo"); + assertEquals("foo", this.simpleCVM.iterator().next()); + assertFalse(this.filteredCVM.iterator().hasNext()); } public void testSetValue() { // add a listener to "activate" the wrapper - this.filteredCollectionHolder.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildFilteredListener()); + this.filteredCVM.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildFilteredListener()); - Collection<String> newCollection = new Vector<String>(); + Collection<String> newCollection = new Vector<>(); newCollection.add("fox"); newCollection.add("baz"); - this.collectionHolder.setValues(newCollection); + this.simpleCVM.setValues(newCollection); - Iterator<String> collectionValues = this.collectionHolder.iterator(); + Iterator<String> collectionValues = this.simpleCVM.iterator(); assertEquals("fox", collectionValues.next()); assertEquals("baz", collectionValues.next()); - Iterator<String> filteredCollectionValues = this.filteredCollectionHolder.iterator(); + Iterator<String> filteredCollectionValues = this.filteredCVM.iterator(); assertEquals("baz", filteredCollectionValues.next()); assertFalse(filteredCollectionValues.hasNext()); } public void testLazyListening() { - assertTrue(((AbstractModel) this.collectionHolder).hasNoCollectionChangeListeners(CollectionValueModel.VALUES)); + assertTrue(((AbstractModel) this.simpleCVM).hasNoCollectionChangeListeners(CollectionValueModel.VALUES)); ChangeListener listener = this.buildFilteredChangeListener(); - this.filteredCollectionHolder.addChangeListener(listener); - assertTrue(((AbstractModel) this.collectionHolder).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); - this.filteredCollectionHolder.removeChangeListener(listener); - assertTrue(((AbstractModel) this.collectionHolder).hasNoCollectionChangeListeners(CollectionValueModel.VALUES)); - - this.filteredCollectionHolder.addCollectionChangeListener(CollectionValueModel.VALUES, listener); - assertTrue(((AbstractModel) this.collectionHolder).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); - this.filteredCollectionHolder.removeCollectionChangeListener(CollectionValueModel.VALUES, listener); - assertTrue(((AbstractModel) this.collectionHolder).hasNoCollectionChangeListeners(CollectionValueModel.VALUES)); + this.filteredCVM.addChangeListener(listener); + assertTrue(((AbstractModel) this.simpleCVM).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + this.filteredCVM.removeChangeListener(listener); + assertTrue(((AbstractModel) this.simpleCVM).hasNoCollectionChangeListeners(CollectionValueModel.VALUES)); + + this.filteredCVM.addCollectionChangeListener(CollectionValueModel.VALUES, listener); + assertTrue(((AbstractModel) this.simpleCVM).hasAnyCollectionChangeListeners(CollectionValueModel.VALUES)); + this.filteredCVM.removeCollectionChangeListener(CollectionValueModel.VALUES, listener); + assertTrue(((AbstractModel) this.simpleCVM).hasNoCollectionChangeListeners(CollectionValueModel.VALUES)); } public void testCollectionChange1() { - this.collectionHolder.addChangeListener(this.buildChangeListener()); - this.filteredCollectionHolder.addChangeListener(this.buildFilteredChangeListener()); + this.simpleCVM.addChangeListener(this.buildChangeListener()); + this.filteredCVM.addChangeListener(this.buildFilteredChangeListener()); this.verifyCollectionChanges(); } public void testCollectionChange2() { - this.collectionHolder.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildListener()); - this.filteredCollectionHolder.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildFilteredListener()); + this.simpleCVM.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildListener()); + this.filteredCVM.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildFilteredListener()); this.verifyCollectionChanges(); } @@ -163,48 +164,48 @@ public class FilteringCollectionValueModelTests extends TestCase { private void verifyCollectionChanges() { clearEvents(); - this.collectionHolder.add("bar"); - Collection<String> tempCollection = new Vector<String>(); + this.simpleCVM.add("bar"); + Collection<String> tempCollection = new Vector<>(); tempCollection.add("bar"); - this.verifyEvent(this.addEvent, this.collectionHolder, tempCollection); - this.verifyEvent(this.filteredAddEvent, this.filteredCollectionHolder, tempCollection); + this.verifyEvent(this.addEvent, this.simpleCVM, tempCollection); + this.verifyEvent(this.filteredAddEvent, this.filteredCVM, tempCollection); clearEvents(); - this.collectionHolder.remove("foo"); + this.simpleCVM.remove("foo"); tempCollection.remove("bar"); tempCollection.add("foo"); - this.verifyEvent(this.removeEvent, this.collectionHolder, tempCollection); + this.verifyEvent(this.removeEvent, this.simpleCVM, tempCollection); assertNull(this.filteredRemoveEvent); clearEvents(); - this.collectionHolder.remove("bar"); + this.simpleCVM.remove("bar"); tempCollection.add("bar"); tempCollection.remove("foo"); - this.verifyEvent(this.removeEvent, this.collectionHolder, tempCollection); - this.verifyEvent(this.filteredRemoveEvent, this.filteredCollectionHolder, tempCollection); + this.verifyEvent(this.removeEvent, this.simpleCVM, tempCollection); + this.verifyEvent(this.filteredRemoveEvent, this.filteredCVM, tempCollection); clearEvents(); - this.collectionHolder.add("foo"); + this.simpleCVM.add("foo"); tempCollection.remove("bar"); tempCollection.add("foo"); - this.verifyEvent(this.addEvent, this.collectionHolder, tempCollection); + this.verifyEvent(this.addEvent, this.simpleCVM, tempCollection); assertNull(this.filteredAddEvent); clearEvents(); - Collection<String> newCollection = new Vector<String>(); + Collection<String> newCollection = new Vector<>(); newCollection.add("fox"); newCollection.add("baz"); - this.collectionHolder.setValues(newCollection); + this.simpleCVM.setValues(newCollection); - this.verifyEvent(this.collectionChangedEvent, this.collectionHolder); + this.verifyEvent(this.collectionChangedEvent, this.simpleCVM); tempCollection.remove("foo"); tempCollection.add("baz"); - this.verifyEvent(this.filteredCollectionChangedEvent, this.filteredCollectionHolder); + this.verifyEvent(this.filteredCollectionChangedEvent, this.filteredCVM); } @@ -303,8 +304,8 @@ public class FilteringCollectionValueModelTests extends TestCase { public void testRemoveFilteredItem() { // build collection with TestItems - SimpleCollectionValueModel<TestItem> tiHolder = new SimpleCollectionValueModel<TestItem>(this.buildCollection2()); - CollectionValueModel<TestItem> filteredTIHolder = new FilteringCollectionValueModel<TestItem>(tiHolder, this.buildFilter2()); + SimpleCollectionValueModel<TestItem> tiHolder = new SimpleCollectionValueModel<>(this.buildCollection2()); + CollectionValueModel<TestItem> filteredTIHolder = new FilteringCollectionValueModel<>(tiHolder, this.buildFilter2()); // add a listener to "activate" the wrapper filteredTIHolder.addCollectionChangeListener(CollectionValueModel.VALUES, this.buildFilteredListener()); @@ -325,7 +326,7 @@ public class FilteringCollectionValueModelTests extends TestCase { } private Collection<TestItem> buildCollection2() { - Collection<TestItem> collection = new Vector<TestItem>(); + Collection<TestItem> collection = new Vector<>(); collection.add(new TestItem("foo")); return collection; } diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/JptCommonUtilityModelValueTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/JptCommonUtilityModelValueTests.java index ea56db400d..ab6f729e9b 100644 --- a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/JptCommonUtilityModelValueTests.java +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/JptCommonUtilityModelValueTests.java @@ -27,9 +27,11 @@ public class JptCommonUtilityModelValueTests { suite.addTestSuite(CollectionAspectAdapterTests.class); suite.addTestSuite(CollectionListValueModelAdapterTests.class); suite.addTestSuite(CollectionPropertyValueModelAdapterTests.class); - suite.addTestSuite(CompositeBooleanPropertyValueModelTests.class); + suite.addTestSuite(CollectionValueModelToolsTests.class); + suite.addTestSuite(CompositeAndBooleanPropertyValueModelTests.class); suite.addTestSuite(CompositeCollectionValueModelTests.class); suite.addTestSuite(CompositeListValueModelTests.class); + suite.addTestSuite(CompositeOrBooleanPropertyValueModelTests.class); suite.addTestSuite(CompositePropertyValueModelTests.class); suite.addTestSuite(DoubleModifiablePropertyValueModelTests.class); suite.addTestSuite(DoublePropertyValueModelTests.class); @@ -42,13 +44,17 @@ public class JptCommonUtilityModelValueTests { suite.addTestSuite(ItemStateListValueModelAdapterTests.class); suite.addTestSuite(ListAspectAdapterTests.class); suite.addTestSuite(ListCollectionValueModelAdapterTests.class); + suite.addTestSuite(ListCompositePropertyValueModelTests.class); suite.addTestSuite(ListCuratorTests.class); + suite.addTestSuite(ListPropertyValueModelAdapterTests.class); + suite.addTestSuite(ListValueModelToolsTests.class); suite.addTestSuite(NullCollectionValueModelTests.class); suite.addTestSuite(NullListValueModelTests.class); suite.addTestSuite(NullPropertyValueModelTests.class); suite.addTestSuite(PropertyAspectAdapterTests.class); suite.addTestSuite(PropertyCollectionValueModelAdapterTests.class); suite.addTestSuite(PropertyListValueModelAdapterTests.class); + suite.addTestSuite(PropertyValueModelToolsTests.class); suite.addTestSuite(ReadOnlyModifiablePropertyValueModelWrapperTests.class); suite.addTestSuite(SetCollectionValueModelTests.class); suite.addTestSuite(SimpleCollectionValueModelTests.class); diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListCompositePropertyValueModelTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListCompositePropertyValueModelTests.java new file mode 100644 index 0000000000..4f28cd8185 --- /dev/null +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListCompositePropertyValueModelTests.java @@ -0,0 +1,304 @@ +/******************************************************************************* + * Copyright (c) 2009, 2016 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.common.utility.tests.internal.model.value; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import org.eclipse.jpt.common.utility.internal.collection.ListTools; +import org.eclipse.jpt.common.utility.internal.exception.RuntimeExceptionHandler; +import org.eclipse.jpt.common.utility.internal.model.ChangeSupport; +import org.eclipse.jpt.common.utility.internal.model.value.ListValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.SimpleListValueModel; +import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; +import org.eclipse.jpt.common.utility.internal.transformer.TransformerAdapter; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.ChangeAdapter; +import org.eclipse.jpt.common.utility.model.listener.ChangeListener; +import org.eclipse.jpt.common.utility.model.value.ListValueModel; +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.tests.internal.TestTools; +import junit.framework.TestCase; + +@SuppressWarnings("nls") +public class ListCompositePropertyValueModelTests + extends TestCase +{ + private SimplePropertyValueModel<String> pvm1; + private ModifiablePropertyValueModel<String> pvm2; + private ModifiablePropertyValueModel<String> pvm3; + private ModifiablePropertyValueModel<String> pvm4; + private List<PropertyValueModel<String>> collection; + private SimpleListValueModel<PropertyValueModel<String>> lvm; + private PropertyValueModel<String> compositePVM; + PropertyChangeEvent event; + + + public ListCompositePropertyValueModelTests(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + this.pvm1 = new SimplePropertyValueModel<>("111"); + this.pvm2 = new SimplePropertyValueModel<>("222"); + this.pvm3 = new SimplePropertyValueModel<>("333"); + this.pvm4 = new SimplePropertyValueModel<>("444"); + this.collection = new ArrayList<>(); + this.collection.add(this.pvm1); + this.collection.add(this.pvm2); + this.collection.add(this.pvm3); + this.collection.add(this.pvm4); + this.lvm = new LocalSimpleListValueModel<>(this.collection); + + this.compositePVM = this.buildCompositePVM(this.lvm); + } + + public static class LocalSimpleListValueModel<E> + extends SimpleListValueModel<E> + { + public LocalSimpleListValueModel(List<E> list) { + super(list); + } + @Override + protected ChangeSupport buildChangeSupport() { + return new ChangeSupport(this, RuntimeExceptionHandler.instance()); + } + } + + private PropertyValueModel<String> buildCompositePVM(ListValueModel<PropertyValueModel<String>> pvms) { + return ListValueModelTools.compositePropertyValueModel(pvms, new LocalTransformer()); + } + + public static class LocalTransformer + extends TransformerAdapter<List<String>, String> + { + @Override + public String transform(List<String> strings) { + StringBuilder sb = new StringBuilder(); + for (Iterator<String> stream = strings.iterator(); stream.hasNext(); ) { + String string = stream.next(); + sb.append(string); + if (stream.hasNext()) { + sb.append("-"); + } + } + return sb.toString(); + } + } + + @Override + protected void tearDown() throws Exception { + TestTools.clear(this); + super.tearDown(); + } + + public void testGetValue1() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertEquals("111-222-333-444", this.compositePVM.getValue()); + } + + public void testGetValue2() { + ArrayList<PropertyValueModel<String>> list = new ArrayList<>(); + ListTools.addAll(list, 0, this.lvm); + this.compositePVM = ListValueModelTools.compositePropertyValueModel(list, new LocalTransformer()); + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertEquals("111-222-333-444", this.compositePVM.getValue()); + } + + public void testGetValue3() { + this.compositePVM = ListValueModelTools.compositePropertyValueModel(new LocalTransformer(), this.pvm1, this.pvm2, this.pvm3, this.pvm4); + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertEquals("111-222-333-444", this.compositePVM.getValue()); + } + + public void testValueAndListeners1() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + assertEquals("111-222-333-444", this.compositePVM.getValue()); + this.compositePVM.removeChangeListener(listener); + assertNull(this.compositePVM.getValue()); + } + + public void testValueAndListeners2() { + assertNull(this.compositePVM.getValue()); + ChangeListener listener = this.buildListener(); + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + assertEquals("111-222-333-444", this.compositePVM.getValue()); + this.compositePVM.removePropertyChangeListener(PropertyValueModel.VALUE, listener); + assertNull(this.compositePVM.getValue()); + } + + public void testPropertyChange1() { + this.compositePVM.addChangeListener(this.buildListener()); + this.verifyPropertyChange(); + } + + public void testPropertyChange2() { + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, this.buildListener()); + this.verifyPropertyChange(); + } + + private void verifyPropertyChange() { + this.event = null; + this.pvm1.setValue("555"); + this.verifyEvent("111-222-333-444", "555-222-333-444"); + + this.event = null; + this.pvm4.setValue("000"); + this.verifyEvent("555-222-333-444", "555-222-333-000"); + } + + public void testListChange1() { + this.compositePVM.addChangeListener(this.buildListener()); + this.verifyListChange(); + } + + public void testListChange2() { + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, this.buildListener()); + this.verifyListChange(); + } + + private void verifyListChange() { + this.event = null; + ModifiablePropertyValueModel<String> pvm7 = new SimplePropertyValueModel<>("777"); + this.lvm.add(pvm7); + this.verifyEvent("111-222-333-444", "111-222-333-444-777"); + + this.event = null; + this.lvm.remove(pvm7); + this.verifyEvent("111-222-333-444-777", "111-222-333-444"); + + this.event = null; + ModifiablePropertyValueModel<String> pvmX = new SimplePropertyValueModel<>("XXX"); + this.lvm.add(2, pvmX); + this.verifyEvent("111-222-333-444", "111-222-XXX-333-444"); + + this.event = null; + this.lvm.move(3, 2); + this.verifyEvent("111-222-XXX-333-444", "111-222-333-XXX-444"); + + this.event = null; + ModifiablePropertyValueModel<String> pvmZ = new SimplePropertyValueModel<>("ZZZ"); + this.lvm.set(3, pvmZ); + this.verifyEvent("111-222-333-XXX-444", "111-222-333-ZZZ-444"); + + this.event = null; + this.lvm.remove(3); + this.verifyEvent("111-222-333-ZZZ-444", "111-222-333-444"); + + this.event = null; + this.lvm.clear(); + this.verifyEvent("111-222-333-444", ""); + + List<PropertyValueModel<String>> c2 = new ArrayList<>(); + c2.add(this.pvm1); + c2.add(this.pvm2); + this.event = null; + this.lvm.setListValues(c2); + this.verifyEvent("", "111-222"); + } + + public void testLazyListening1() { + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + ChangeListener listener = this.buildListener(); + + this.compositePVM.addChangeListener(listener); + assertTrue(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + + this.compositePVM.removeChangeListener(listener); + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + } + + public void testLazyListening2() { + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + ChangeListener listener = this.buildListener(); + + this.compositePVM.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + assertTrue(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + + this.compositePVM.removePropertyChangeListener(PropertyValueModel.VALUE, listener); + assertFalse(this.pvm1.hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + } + + public void testCtor_NPE1() { + boolean exCaught = false; + try { + this.compositePVM = this.buildCompositePVM(null); + fail("bogus: " + this.compositePVM); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE2() { + boolean exCaught = false; + try { + this.compositePVM = ListValueModelTools.compositePropertyValueModel((ListValueModel<PropertyValueModel<String>>) this.lvm, (TransformerAdapter<List<String>, String>) null); + fail("bogus: " + this.compositePVM); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testNullPVM() { + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + boolean exCaught = false; + try { + this.lvm.add(null); + fail("bogus: " + this.lvm); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testDuplicatePVM() { + ChangeListener listener = this.buildListener(); + this.compositePVM.addChangeListener(listener); + boolean exCaught = false; + try { + this.lvm.add(this.pvm1); + fail("bogus: " + this.lvm); + } catch (IllegalStateException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + private ChangeListener buildListener() { + return new ChangeAdapter() { + @Override + public void propertyChanged(PropertyChangeEvent e) { + ListCompositePropertyValueModelTests.this.event = e; + } + }; + } + + private void verifyEvent(String oldValue, String newValue) { + assertNotNull(this.event); + assertEquals(this.compositePVM, this.event.getSource()); + assertEquals(PropertyValueModel.VALUE, this.event.getPropertyName()); + assertEquals(oldValue, this.event.getOldValue()); + assertEquals(newValue, this.event.getNewValue()); + } +} diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListPropertyValueModelAdapterTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListPropertyValueModelAdapterTests.java new file mode 100644 index 0000000000..84ffde9534 --- /dev/null +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListPropertyValueModelAdapterTests.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2007, 2016 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.common.utility.tests.internal.model.value; + +import java.util.Arrays; +import java.util.List; +import org.eclipse.jpt.common.utility.internal.ObjectTools; +import org.eclipse.jpt.common.utility.internal.model.AbstractModel; +import org.eclipse.jpt.common.utility.internal.model.value.ListPluggablePropertyValueModelAdapter; +import org.eclipse.jpt.common.utility.internal.model.value.ListValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.PropertyValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.SimpleListValueModel; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.ChangeAdapter; +import org.eclipse.jpt.common.utility.model.listener.ChangeListener; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; +import org.eclipse.jpt.common.utility.model.value.ListValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import org.eclipse.jpt.common.utility.tests.internal.TestTools; +import org.eclipse.jpt.common.utility.transformer.Transformer; +import junit.framework.TestCase; + +@SuppressWarnings("nls") +public class ListPropertyValueModelAdapterTests + extends TestCase +{ + private PropertyValueModel<Boolean> adapter; + private SimpleListValueModel<String> listModel; + PropertyChangeEvent event; + + public ListPropertyValueModelAdapterTests(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + this.listModel = new SimpleListValueModel<>(); + this.adapter = ListValueModelTools.propertyValueModel(this.listModel, new LocalTransformer(2, "666")); + this.event = null; + } + + @Override + protected void tearDown() throws Exception { + TestTools.clear(this); + super.tearDown(); + } + + private boolean booleanValue() { + Boolean value = this.adapter.getValue(); + return (value != null) && value.booleanValue(); + } + + private boolean listModelContains(int index, String value) { + return (this.listModel.size() > index) && ObjectTools.equals(this.listModel.get(index), value); + } + + public void testValue() { + assertNull(this.adapter.getValue()); + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, new PropertyChangeListener() { + public void propertyChanged(PropertyChangeEvent e) {/* OK */} + }); + assertFalse(this.booleanValue()); + assertFalse(this.listModelContains(2, "666")); + + this.listModel.add("111"); + assertFalse(this.booleanValue()); + + this.listModel.add("222"); + assertFalse(this.booleanValue()); + + this.listModel.add("666"); + assertTrue(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + + this.listModel.remove("666"); + assertFalse(this.booleanValue()); + assertFalse(this.listModelContains(2, "666")); + + this.listModel.add("666"); + assertTrue(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + + this.listModel.clear(); + assertFalse(this.booleanValue()); + assertFalse(this.listModelContains(2, "666")); + + this.listModel.add("111"); + this.listModel.add("222"); + this.listModel.add("666"); + assertTrue(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + + this.listModel.set(2, "333"); + assertFalse(this.booleanValue()); + assertFalse(this.listModelContains(2, "666")); + + this.listModel.set(2, "666"); + assertTrue(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + + this.listModel.move(0, 2); + assertFalse(this.booleanValue()); + assertTrue(this.listModelContains(0, "666")); + + this.listModel.setListValues(Arrays.asList("111", "222", "666")); + assertTrue(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + } + + public void testEventFiring() { + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, new PropertyChangeListener() { + public void propertyChanged(PropertyChangeEvent e) { + ListPropertyValueModelAdapterTests.this.event = e; + } + }); + assertNull(this.event); + + this.listModel.add("111"); + assertNull(this.event); + + this.listModel.add("222"); + assertNull(this.event); + + this.listModel.add("666"); + this.verifyEvent(false, true); + + this.listModel.remove("666"); + this.verifyEvent(true, false); + + this.listModel.add("666"); + this.verifyEvent(false, true); + + this.listModel.clear(); + this.verifyEvent(true, false); + } + + private void verifyEvent(boolean oldValue, boolean newValue) { + assertEquals(this.adapter, this.event.getSource()); + assertEquals(Boolean.valueOf(oldValue), this.event.getOldValue()); + assertEquals(Boolean.valueOf(newValue), this.event.getNewValue()); + this.event = null; + } + + public void testStaleValue() { + PropertyChangeListener listener = new PropertyChangeListener() { + public void propertyChanged(PropertyChangeEvent e) {/* OK */} + }; + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + this.listModel.add("111"); + this.listModel.add("222"); + this.listModel.add("666"); + assertTrue(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + + this.adapter.removePropertyChangeListener(PropertyValueModel.VALUE, listener); + assertFalse(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + assertTrue(this.booleanValue()); + assertTrue(this.listModelContains(2, "666")); + } + + public void testHasListeners() { + assertFalse(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + assertFalse(((AbstractModel) this.listModel).hasAnyListChangeListeners(ListValueModel.LIST_VALUES)); + + ChangeListener listener = new ChangeAdapter() { + @Override + public void propertyChanged(PropertyChangeEvent e) {/* OK */} + }; + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + assertTrue(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + assertTrue(((AbstractModel) this.listModel).hasAnyListChangeListeners(ListValueModel.LIST_VALUES)); + + this.adapter.removePropertyChangeListener(PropertyValueModel.VALUE, listener); + assertFalse(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + assertFalse(((AbstractModel) this.listModel).hasAnyListChangeListeners(ListValueModel.LIST_VALUES)); + + this.adapter.addChangeListener(listener); + assertTrue(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + assertTrue(((AbstractModel) this.listModel).hasAnyListChangeListeners(ListValueModel.LIST_VALUES)); + + this.adapter.removeChangeListener(listener); + assertFalse(((AbstractModel) this.adapter).hasAnyPropertyChangeListeners(PropertyValueModel.VALUE)); + assertFalse(((AbstractModel) this.listModel).hasAnyListChangeListeners(ListValueModel.LIST_VALUES)); + } + + public void testToString1() { + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, new PropertyChangeListener() { + public void propertyChanged(PropertyChangeEvent e) {/* OK */} + }); + assertTrue(this.adapter.toString().endsWith("(false)")); + this.listModel.add("111"); + this.listModel.add("222"); + this.listModel.add("666"); + assertTrue(this.adapter.toString().endsWith("(true)")); + } + + public void testToString3() { + ListPluggablePropertyValueModelAdapter.Factory<String, Boolean> f = new ListPluggablePropertyValueModelAdapter.Factory<>(this.listModel, new LocalTransformer(2, "666")); + assertTrue(f.toString().indexOf("Factory") != -1); + } + + public void testToString4() { + PropertyChangeListener listener = new PropertyChangeListener() { + public void propertyChanged(PropertyChangeEvent e) {/* OK */} + }; + this.adapter.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + Object a = ObjectTools.get(this.adapter, "adapter"); + Object l = ObjectTools.get(a, "listener"); + assertTrue(l.toString().indexOf("AdapterListener") != -1); + } + + public void testCtor_NPE1A() { + Object object; + boolean exCaught = false; + try { + object = ListValueModelTools.propertyValueModel(null, new LocalTransformer(2, "666")); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE1B() { + Object object; + boolean exCaught = false; + try { + object = ListValueModelTools.propertyValueModel(null, new LocalTransformer(2, "666")); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE2A() { + Object object; + boolean exCaught = false; + try { + object = ListValueModelTools.propertyValueModel(this.listModel, null); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE2B() { + Object object; + boolean exCaught = false; + try { + object = ListValueModelTools.propertyValueModel(this.listModel, null); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testCtor_NPE4() { + Object object; + boolean exCaught = false; + try { + object = PropertyValueModelTools.propertyValueModel(null); + fail("bogus: " + object); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + + // ********** member class ********** + + /** + * Transform the list to <code>true</code> if it contains the specified item + * at the specified index, otherwise transform it to <code>false</code>. + */ + static class LocalTransformer + implements Transformer<List<String>, Boolean> + { + private final int index; + private final String item; + + LocalTransformer(int index, String item) { + super(); + this.index = index; + this.item = item; + } + + public Boolean transform(List<String> list) { + return Boolean.valueOf(this.transform_(list)); + } + + public boolean transform_(List<String> list) { + return (list.size() > this.index) && ObjectTools.equals(this.item, list.get(this.index)); + } + + @Override + public String toString() { + return ObjectTools.toString(this, this.item); + } + } +} diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListValueModelToolsTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListValueModelToolsTests.java new file mode 100644 index 0000000000..12ce703f86 --- /dev/null +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/ListValueModelToolsTests.java @@ -0,0 +1,438 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.tests.internal.model.value; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import org.eclipse.jpt.common.utility.internal.ClassTools; +import org.eclipse.jpt.common.utility.internal.closure.BooleanClosure; +import org.eclipse.jpt.common.utility.internal.model.value.ListValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.SimpleListValueModel; +import org.eclipse.jpt.common.utility.internal.predicate.PredicateTools; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeAdapter; +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import junit.framework.TestCase; + +@SuppressWarnings("nls") +public class ListValueModelToolsTests + extends TestCase +{ + public ListValueModelToolsTests(String name) { + super(name); + } + + public void testFirstElementPVMAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<String> pvm = ListValueModelTools.firstElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertNull(pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertEquals("foo", pvm.getValue()); + assertEquals("foo", listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertEquals("foo", pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("foo"); + assertEquals("bar", pvm.getValue()); + assertEquals("bar", listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + } + + public void testLastElementPVMAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<String> pvm = ListValueModelTools.lastElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertNull(pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertEquals("foo", pvm.getValue()); + assertEquals("foo", listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertEquals("bar", pvm.getValue()); + assertEquals("bar", listener.event.getNewValue()); + + listener.event = null; + cvm.remove("foo"); + assertEquals("bar", pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + } + + public void testSingleElementPVMAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<String> pvm = ListValueModelTools.singleElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertNull(pvm.getValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertEquals("foo", pvm.getValue()); + assertEquals("foo", listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + + listener.event = null; + cvm.remove("foo"); + assertEquals("bar", pvm.getValue()); + assertEquals("bar", listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertNull(pvm.getValue()); + assertNull(listener.event.getNewValue()); + } + + public void testIsNotEmptyPropertyValueModelAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<Boolean> pvm = ListValueModelTools.isNotEmptyPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + } + + public void testIsNotEmptyModifiablePropertyValueModelAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + BooleanClosure.Adapter adapter = new NotEmptyBooleanClosureAdapter(cvm); + ModifiablePropertyValueModel<Boolean> pvm = ListValueModelTools.isNotEmptyModifiablePropertyValueModel(cvm, adapter); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + pvm.setValue(Boolean.FALSE); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + assertEquals(0, cvm.size()); + + listener.event = null; + pvm.setValue(Boolean.TRUE); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + assertEquals(2, cvm.size()); + assertTrue(cvm.contains("baz")); + assertTrue(cvm.contains("xxx")); + + listener.event = null; + cvm.remove("baz"); + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("xxx"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + } + + public static class NotEmptyBooleanClosureAdapter + implements BooleanClosure.Adapter + { + final SimpleListValueModel<String> cvm; + public NotEmptyBooleanClosureAdapter(SimpleListValueModel<String> cvm) { + this.cvm = cvm; + } + public void execute(boolean argument) { + if (argument) { + ArrayList<String> list = new ArrayList<>(); + list.add("baz"); + list.add("xxx"); + this.cvm.setListValues(list); + } else { + this.cvm.clear(); + } + } + } + + public void testIsEmptyPropertyValueModelAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<Boolean> pvm = ListValueModelTools.isEmptyPropertyValueModel(cvm); + this.verifyIsEmptyPropertyValueModelAdapter(cvm, pvm); + } + + private void verifyIsEmptyPropertyValueModelAdapter(SimpleListValueModel<String> cvm, PropertyValueModel<Boolean> pvm) { + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + } + + public void testBooleanPVM() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<Boolean> pvm = ListValueModelTools.booleanPropertyValueModel(cvm, PredicateTools.collectionIsEmptyPredicate()); + this.verifyIsEmptyPropertyValueModelAdapter(cvm, pvm); + } + + public void testIsEmptyModifiablePropertyValueModelAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + BooleanClosure.Adapter adapter = new EmptyBooleanClosureAdapter(cvm); + ModifiablePropertyValueModel<Boolean> pvm = ListValueModelTools.isEmptyModifiablePropertyValueModel(cvm, adapter); + this.verifyIsEmptyModifiablePropertyValueModelAdapter(cvm, pvm); + } + + public void testBooleanModifiablePVM() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + BooleanClosure.Adapter adapter = new EmptyBooleanClosureAdapter(cvm); + ModifiablePropertyValueModel<Boolean> pvm = ListValueModelTools.booleanModifiablePropertyValueModel(cvm, PredicateTools.collectionIsEmptyPredicate(), adapter); + this.verifyIsEmptyPropertyValueModelAdapter(cvm, pvm); + } + + private void verifyIsEmptyModifiablePropertyValueModelAdapter(SimpleListValueModel<String> cvm, ModifiablePropertyValueModel<Boolean> pvm) { + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertTrue(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + pvm.setValue(Boolean.TRUE); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + assertEquals(0, cvm.size()); + + listener.event = null; + pvm.setValue(Boolean.FALSE); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + assertEquals(2, cvm.size()); + assertTrue(cvm.contains("baz")); + assertTrue(cvm.contains("xxx")); + + listener.event = null; + cvm.remove("baz"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.remove("xxx"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + } + + public static class EmptyBooleanClosureAdapter + implements BooleanClosure.Adapter + { + final SimpleListValueModel<String> cvm; + public EmptyBooleanClosureAdapter(SimpleListValueModel<String> cvm) { + this.cvm = cvm; + } + public void execute(boolean argument) { + if (argument) { + this.cvm.clear(); + } else { + ArrayList<String> list = new ArrayList<>(); + list.add("baz"); + list.add("xxx"); + this.cvm.setListValues(list); + } + } + } + + public void testContainsSingleElementPropertyValueModelAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<Boolean> pvm = ListValueModelTools.containsSingleElementPropertyValueModel(cvm); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("foo"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + } + + public void testSizeEqualsPropertyValueModelAdapter() { + SimpleListValueModel<String> cvm = new SimpleListValueModel<>(); + PropertyValueModel<Boolean> pvm = ListValueModelTools.sizeEqualsPropertyValueModel(cvm, 2); + LocalPropertyChangeListener listener = new LocalPropertyChangeListener(); + pvm.addPropertyChangeListener(PropertyValueModel.VALUE, listener); + + listener.event = null; + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + + listener.event = null; + cvm.add("bar"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.add("baz"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("baz"); + assertTrue(pvm.getValue().booleanValue()); + assertEquals(Boolean.TRUE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("foo"); + assertFalse(pvm.getValue().booleanValue()); + assertEquals(Boolean.FALSE, listener.event.getNewValue()); + + listener.event = null; + cvm.remove("bar"); + assertFalse(pvm.getValue().booleanValue()); + assertNull(listener.event); + } + + public static class LocalPropertyChangeListener + extends PropertyChangeAdapter + { + public PropertyChangeEvent event; + public LocalPropertyChangeListener() { + super(); + } + @Override + public void propertyChanged(PropertyChangeEvent e) { + this.event = e; + } + } + + public void testConstructor() { + boolean exCaught = false; + try { + Object o = ClassTools.newInstance(ListValueModelTools.class); + fail("bogus: " + o); //$NON-NLS-1$ + } catch (RuntimeException ex) { + if (ex.getCause() instanceof InvocationTargetException) { + if (ex.getCause().getCause() instanceof UnsupportedOperationException) { + exCaught = true; + } + } + } + assertTrue(exCaught); + } +} diff --git a/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/PropertyValueModelToolsTests.java b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/PropertyValueModelToolsTests.java new file mode 100644 index 0000000000..1943731ab0 --- /dev/null +++ b/common/tests/org.eclipse.jpt.common.utility.tests/src/org/eclipse/jpt/common/utility/tests/internal/model/value/PropertyValueModelToolsTests.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2016 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.common.utility.tests.internal.model.value; + +import java.lang.reflect.InvocationTargetException; +import org.eclipse.jpt.common.utility.closure.Closure; +import org.eclipse.jpt.common.utility.internal.ClassTools; +import org.eclipse.jpt.common.utility.internal.model.value.CollectionValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.PluggableModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.internal.model.value.PluggableModifiablePropertyValueModel.Adapter; +import org.eclipse.jpt.common.utility.internal.model.value.PluggablePropertyValueModel; +import org.eclipse.jpt.common.utility.internal.model.value.PropertyValueModelTools; +import org.eclipse.jpt.common.utility.internal.model.value.SimpleCollectionValueModel; +import org.eclipse.jpt.common.utility.internal.model.value.SimplePropertyValueModel; +import org.eclipse.jpt.common.utility.internal.transformer.TransformerAdapter; +import org.eclipse.jpt.common.utility.model.event.PropertyChangeEvent; +import org.eclipse.jpt.common.utility.model.listener.PropertyChangeListener; +import org.eclipse.jpt.common.utility.model.value.ModifiablePropertyValueModel; +import org.eclipse.jpt.common.utility.model.value.PropertyValueModel; +import junit.framework.TestCase; + +@SuppressWarnings("nls") +public class PropertyValueModelToolsTests + extends TestCase +{ + + public PropertyValueModelToolsTests(String name) { + super(name); + } + + public void testModifiablePropertyValueModel() { + ModifiablePropertyValueModel<String> doubleStringModel = new SimplePropertyValueModel<>("foofoo"); + PluggableModifiablePropertyValueModel.Adapter.Factory<String> factory = new HalfStringModelAdapter.Factory(doubleStringModel); + ModifiablePropertyValueModel<String> halfStringModel = PropertyValueModelTools.modifiablePropertyValueModel(factory); + HalfStringListener halfStringListener = new HalfStringListener(); + halfStringModel.addPropertyChangeListener(PropertyValueModel.VALUE, halfStringListener); + + halfStringListener.event = null; + assertEquals("foofoo", doubleStringModel.getValue()); + assertEquals("foo", halfStringModel.getValue()); + assertNull(halfStringListener.event); + + halfStringListener.event = null; + halfStringModel.setValue("bar"); + assertEquals("bar", halfStringModel.getValue()); + assertEquals("barbar", doubleStringModel.getValue()); + assertEquals("bar", halfStringListener.event.getNewValue()); + + halfStringListener.event = null; + halfStringModel.setValue("bar"); + assertEquals("bar", halfStringModel.getValue()); + assertEquals("barbar", doubleStringModel.getValue()); + assertNull(halfStringListener.event); + + halfStringListener.event = null; + doubleStringModel.setValue("xxxxxx"); + assertEquals("xxx", halfStringModel.getValue()); + assertEquals("xxxxxx", doubleStringModel.getValue()); + assertEquals("xxx", halfStringListener.event.getNewValue()); + + halfStringListener.event = null; + halfStringModel.removePropertyChangeListener(PropertyValueModel.VALUE, halfStringListener); + assertNull(halfStringModel.getValue()); + assertEquals("xxxxxx", doubleStringModel.getValue()); + assertNull(halfStringListener.event); + } + + public static class HalfStringListener + implements PropertyChangeListener + { + public PropertyChangeEvent event; + public void propertyChanged(PropertyChangeEvent e) { + this.event = e; + } + } + + public static class HalfStringModelAdapter + implements PluggableModifiablePropertyValueModel.Adapter<String> + { + private final ModifiablePropertyValueModel<String> stringModel; + private final PluggableModifiablePropertyValueModel.Adapter.Listener<String> listener; + private final PropertyChangeListener stringListener; + private volatile String value; + + public HalfStringModelAdapter(ModifiablePropertyValueModel<String> stringModel, PluggableModifiablePropertyValueModel.Adapter.Listener<String> listener) { + super(); + this.stringModel = stringModel; + this.listener = listener; + this.stringListener = new StringListener(); + this.value = null; + } + + public void engageModel() { + this.stringModel.addPropertyChangeListener(PropertyValueModel.VALUE, this.stringListener); + String v = this.stringModel.getValue(); + this.value = v.substring(v.length() / 2); + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + this.stringModel.setValue(value + value); + } + + public void disengageModel() { + this.value = null; + this.stringModel.removePropertyChangeListener(PropertyValueModel.VALUE, this.stringListener); + } + + void stringChanged(String newStringValue) { + String newValue = newStringValue.substring(newStringValue.length() / 2); + this.value = newValue; + this.listener.valueChanged(newValue); + } + + public class StringListener + implements PropertyChangeListener + { + public void propertyChanged(PropertyChangeEvent event) { + HalfStringModelAdapter.this.stringChanged((String) event.getNewValue()); + } + } + + public static class Factory + implements PluggableModifiablePropertyValueModel.Adapter.Factory<String> + { + private final ModifiablePropertyValueModel<String> stringModel; + public Factory(ModifiablePropertyValueModel<String> stringModel) { + super(); + this.stringModel = stringModel; + } + public Adapter<String> buildAdapter(PluggableModifiablePropertyValueModel.Adapter.Listener<String> listener) { + return new HalfStringModelAdapter(this.stringModel, listener); + } + } + } + + public void testPluggableModifiablePropertyValueModel_NPE() { + PluggablePropertyValueModel.Adapter.Factory<String> factory = CollectionValueModelTools.pluggablePropertyValueModelAdapterFactory(new SimpleCollectionValueModel<>(), new TransformerAdapter<>()); + Closure<String> closure = null; + boolean exCaught = false; + try { + ModifiablePropertyValueModel<String> pvm = PropertyValueModelTools.pluggableModifiablePropertyValueModel(factory, closure); + fail("bogus: " + pvm); + } catch (NullPointerException ex) { + exCaught = true; + } + assertTrue(exCaught); + } + + public void testConstructor() { + boolean exCaught = false; + try { + Object o = ClassTools.newInstance(PropertyValueModelTools.class); + fail("bogus: " + o); + } catch (RuntimeException ex) { + if (ex.getCause() instanceof InvocationTargetException) { + if (ex.getCause().getCause() instanceof UnsupportedOperationException) { + exCaught = true; + } + } + } + assertTrue(exCaught); + } +} |