# HG changeset patch # User Frank Benoit # Date 1239701729 -7200 # Node ID 0a55d2d5a9467d09be46097d2244110b49a516dc # Parent f05e6e8b2f2dcebd7f0e1d59bf3c6e71f7aa409b Added file for databinding diff -r f05e6e8b2f2d -r 0a55d2d5a946 base/src/java/lang/Thread.d --- a/base/src/java/lang/Thread.d Sun Apr 12 12:27:13 2009 +0200 +++ b/base/src/java/lang/Thread.d Tue Apr 14 11:35:29 2009 +0200 @@ -17,7 +17,7 @@ } private TThread thread; private Runnable runnable; - + private bool interrupted_ = false; version(Tango){ private alias tango.core.Thread.ThreadLocal!(Thread) TTLS; private static TTLS tls; @@ -97,7 +97,7 @@ // assert( MIN_PRIORITY < MAX_PRIORITY ); // assert( tango.core.Thread.Thread.PRIORITY_MIN < tango.core.Thread.Thread.PRIORITY_MAX ); auto scaledPrio = (newPriority-MIN_PRIORITY) * (TThread.PRIORITY_MAX-TThread.PRIORITY_MIN) / (MAX_PRIORITY-MIN_PRIORITY) +TThread.PRIORITY_MIN; - getDwtLogger().trace( __FILE__, __LINE__, "Thread.setPriority: scale ({} {} {}) -> ({} {} {})", MIN_PRIORITY, newPriority, MAX_PRIORITY, TThread.PRIORITY_MIN, scaledPrio, TThread.PRIORITY_MAX); +// getDwtLogger().trace( __FILE__, __LINE__, "Thread.setPriority: scale ({} {} {}) -> ({} {} {})", MIN_PRIORITY, newPriority, MAX_PRIORITY, TThread.PRIORITY_MIN, scaledPrio, TThread.PRIORITY_MAX); // thread.priority( scaledPrio ); } @@ -140,12 +140,17 @@ } void interrupt() { + interrupted_ = true; implMissing(__FILE__,__LINE__); } static bool interrupted() { - implMissing(__FILE__,__LINE__); - return false; + auto t = currentThread(); + synchronized(t){ + bool res = t.interrupted_; + t.interrupted_ = false; + return res; + } } public void run(){ diff -r f05e6e8b2f2d -r 0a55d2d5a946 base/src/java/util/HashSet.d --- a/base/src/java/util/HashSet.d Sun Apr 12 12:27:13 2009 +0200 +++ b/base/src/java/util/HashSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -25,13 +25,26 @@ } } public this(Collection c){ - implMissing( __FILE__, __LINE__ ); + version(Tango){ + set = new SetType(); + addAll(c); + } else { // Phobos + implMissing( __FILE__, __LINE__ ); + } } public this(int initialCapacity){ - implMissing( __FILE__, __LINE__ ); + version(Tango){ + set = new SetType(); + } else { // Phobos + implMissing( __FILE__, __LINE__ ); + } } public this(int initialCapacity, float loadFactor){ - implMissing( __FILE__, __LINE__ ); + version(Tango){ + set = new SetType(loadFactor); + } else { // Phobos + implMissing( __FILE__, __LINE__ ); + } } public bool add(Object o){ version(Tango){ @@ -45,8 +58,11 @@ return add(stringcast(o)); } public bool addAll(Collection c){ - implMissing( __FILE__, __LINE__ ); - return false; + bool res = false; + foreach( o; c ){ + res |= add(o); + } + return res; } public void clear(){ version(Tango){ diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/AggregateValidationStatus.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/AggregateValidationStatus.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matt Carter - bug 182822 + * Boris Bokowski - bug 218269 + * Matthew Hall - bug 218269 + *******************************************************************************/ +module org.eclipse.core.databinding.AggregateValidationStatus; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservableCollection; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.value.ComputedValue; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +/** + * This class can be used to aggregate status values from a data binding context + * into a single status value. Instances of this class can be used as an + * observable value with a value type of {@link IStatus}, or the static methods + * can be called directly if an aggregated status result is only needed once. + * + * @since 1.0 + * + */ +public final class AggregateValidationStatus : IObservableValue { + + private IObservableValue implementation; + + /** + * Constant denoting an aggregation strategy that merges multiple non-OK + * status objects in a {@link MultiStatus}. Returns an OK status result if + * all statuses from the given validation status providers are the an OK + * status. Returns a single status if there is only one non-OK status. + * + * @see #getStatusMergedcast(Collection) + */ + public static final int MERGED = 1; + + /** + * Constant denoting an aggregation strategy that always returns the most + * severe status from the given validation status providers. If there is + * more than one status at the same severity level, it picks the first one + * it encounters. + * + * @see #getStatusMaxSeveritycast(Collection) + */ + public static final int MAX_SEVERITY = 2; + + /** + * Creates a new aggregate validation status observable for the given data + * binding context. + * + * @param dbc + * a data binding context + * @param strategy + * a strategy constant, one of {@link #MERGED} or + * {@link #MAX_SEVERITY}. + * @since 1.1 + */ + public this(DataBindingContext dbc, int strategy) { + this(dbc.getValidationRealm(), dbc.getValidationStatusProviders(), + strategy); + } + + /** + * @param validationStatusProviders + * an observable collection containing elements of type + * {@link ValidationStatusProvider} + * @param strategy + * a strategy constant, one of {@link #MERGED} or + * {@link #MAX_SEVERITY}. + * @see DataBindingContext#getValidationStatusProviders() + */ + public this( + IObservableCollection validationStatusProviders, strategy) { + this(Realm.getDefault(), validationStatusProviders, strategy); + } + + /** + * @param realm + * Realm + * @param validationStatusProviders + * an observable collection containing elements of type + * {@link ValidationStatusProvider} + * @param strategy + * a strategy constant, one of {@link #MERGED} or + * {@link #MAX_SEVERITY}. + * @see DataBindingContext#getValidationStatusProviders() + * @since 1.1 + */ + public this(Realm realm, + IObservableCollection validationStatusProviders, int strategy) { + if (strategy is MERGED) { + implementation = new class(realm, IStatus.classinfo, validationStatusProviders) ComputedValue { + IObservableCollection validationStatusProviders_; + this(Realm r, ClassInfo c, IObservableCollection v){ + super(r, c); + validationStatusProviders_=v; + } + protected Object calculate() { + return getStatusMerged(validationStatusProviders_); + } + }; + } else { + implementation = new class(realm, IStatus.classinfo, validationStatusProviders) ComputedValue { + IObservableCollection validationStatusProviders_; + this(Realm r, ClassInfo c, IObservableCollection v){ + super(r, c); + validationStatusProviders_=v; + } + protected Object calculate() { + return getStatusMaxSeverity(validationStatusProviders_); + } + }; + } + } + + /** + * @param listener + * @see org.eclipse.core.databinding.observable.IObservable#addChangeListener(org.eclipse.core.databinding.observable.IChangeListener) + */ + public void addChangeListener(IChangeListener listener) { + implementation.addChangeListener(listener); + } + + /** + * @param listener + * @see org.eclipse.core.databinding.observable.IObservable#addStaleListener(org.eclipse.core.databinding.observable.IStaleListener) + */ + public void addStaleListener(IStaleListener listener) { + implementation.addStaleListener(listener); + } + + /** + * @param listener + * @see org.eclipse.core.databinding.observable.value.IObservableValue#addValueChangeListener(org.eclipse.core.databinding.observable.value.IValueChangeListener) + */ + public void addValueChangeListener(IValueChangeListener listener) { + implementation.addValueChangeListener(listener); + } + + public void dispose() { + implementation.dispose(); + } + + public Realm getRealm() { + return implementation.getRealm(); + } + + public Object getValue() { + return implementation.getValue(); + } + + public Object getValueType() { + return implementation.getValueType(); + } + + public bool isStale() { + return implementation.isStale(); + } + + public void removeChangeListener(IChangeListener listener) { + implementation.removeChangeListener(listener); + } + + public void removeStaleListener(IStaleListener listener) { + implementation.removeStaleListener(listener); + } + + public void removeValueChangeListener(IValueChangeListener listener) { + implementation.removeValueChangeListener(listener); + } + + public void setValue(Object value) { + implementation.setValue(value); + } + + /** + * Returns a status object that merges multiple non-OK status objects in a + * {@link MultiStatus}. Returns an OK status result if all statuses from + * the given validation status providers are the an OK status. Returns a + * single status if there is only one non-OK status. + * + * @param validationStatusProviders + * a collection of validation status providers + * @return a merged status + */ + public static IStatus getStatusMerged(Collection validationStatusProviders) { + List statuses = new ArrayList(); + for (Iterator it = validationStatusProviders.iterator(); it.hasNext();) { + ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) it + .next(); + IStatus status = cast(IStatus) validationStatusProvider + .getValidationStatus().getValue(); + if (!status.isOK()) { + statuses.add(status); + } + } + if (statuses.size() is 1) { + return cast(IStatus) statuses.get(0); + } + if (!statuses.isEmpty()) { + MultiStatus result = new MultiStatus(Policy.JFACE_DATABINDING, 0, + BindingMessages + .getStringcast(BindingMessages.MULTIPLE_PROBLEMS), null); + for (Iterator it = statuses.iterator(); it.hasNext();) { + IStatus status = cast(IStatus) it.next(); + result.merge(status); + } + return result; + } + return Status.OK_STATUS; + } + + /** + * Returns a status that always returns the most severe status from the + * given validation status providers. If there is more than one status at + * the same severity level, it picks the first one it encounters. + * + * @param validationStatusProviders + * a collection of validation status providers + * @return a single status reflecting the most severe status from the given + * validation status providers + */ + public static IStatus getStatusMaxSeverity( + Collection validationStatusProviders) { + int maxSeverity = IStatus.OK; + IStatus maxStatus = Status.OK_STATUS; + for (Iterator it = validationStatusProviders.iterator(); it.hasNext();) { + ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) it + .next(); + IStatus status = cast(IStatus) validationStatusProvider + .getValidationStatus().getValue(); + if (status.getSeverity() > maxSeverity) { + maxSeverity = status.getSeverity(); + maxStatus = status; + } + } + return maxStatus; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/Binding.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/Binding.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 159768 + * Boris Bokowski - bug 218269 + * Matthew Hall - bug 218269 + *******************************************************************************/ + +module org.eclipse.core.databinding.Binding; + +import java.lang.all; + +import java.util.Collections; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.Observables; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.value.IObservableValue; + +/** + * This abstract class represents a binding between a model and a target. Newly + * created instances need to be added to a data binding context using + * {@link #initcast(DataBindingContext)}. + * + * @since 1.0 + */ +public abstract class Binding : ValidationStatusProvider { + + protected DataBindingContext context; + private IObservable target; + private IObservable model; + + /** + * Creates a new binding. + * + * @param target target observable + * @param model model observable + */ + public this(IObservable target, IObservable model) { + this.target = target; + this.model = model; + } + + /** + * Initializes this binding with the given context and adds it to the list + * of bindings of the context. + *

+ * Subclasses may extend, but must call the super implementation. + *

+ * + * @param context + */ + public final void init(DataBindingContext context) { + this.context = context; + preInit(); + context.addBinding(this); + postInit(); + } + + /** + * Called by {@link #initcast(DataBindingContext)} after setting + * {@link #context} but before adding this binding to the context. + * Subclasses may use this method to perform initialization that could not + * be done in the constructor. Care should be taken not to cause any events + * while running this method. + */ + protected abstract void preInit(); + + /** + * Called by {@link #initcast(DataBindingContext)} after adding this binding to + * the context. Subclasses may use this method to perform initialization + * that may cause events to be fired, including BindingEvents that are + * forwarded to the data binding context. + */ + protected abstract void postInit(); + + /** + * @return an observable value containing the current validation status + */ + public abstract IObservableValue getValidationStatus(); + + /** + * Updates the model's state from the target's state at the next reasonable + * opportunity. There is no guarantee that the state will have been updated + * by the time this call returns. + */ + public abstract void updateTargetToModel(); + + /** + * Updates the target's state from the model's state at the next reasonable + * opportunity. There is no guarantee that the state will have been updated + * by the time this call returns. + */ + public abstract void updateModelToTarget(); + + /** + * Validates the target's state at the next reasonable + * opportunity. There is no guarantee that the validation status will have been updated + * by the time this call returns. + */ + public abstract void validateTargetToModel(); + + /** + * Validates the model's state at the next reasonable + * opportunity. There is no guarantee that the validation status will have been updated + * by the time this call returns. + */ + public abstract void validateModelToTarget(); + + /** + * Disposes of this Binding. Subclasses may extend, but must call super.dispose(). + */ + public void dispose() { + if (context !is null) { + context.removeBinding(this); + } + context = null; + target = null; + model = null; + super.dispose(); + } + + /** + * @param context + */ + /* package */ void setDataBindingContext(DataBindingContext context) { + this.context = context; + } + + /** + * @return target observable + */ + public IObservable getTarget() { + return target; + } + + /** + * @since 1.1 + */ + public IObservableList getTargets() { + return Observables.staticObservableList(context.getValidationRealm(), + Collections.singletonList(target)); + } + + /** + * @return model observable + */ + public IObservable getModel() { + return model; + } + + /** + * @since 1.1 + */ + public IObservableList getModels() { + return Observables.staticObservableList(context.getValidationRealm(), + Collections.singletonList(model)); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingException.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingException.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.BindingException; + +import java.lang.all; + +import java.io.PrintStream; +import java.io.PrintWriter; + +/** + * An unchecked exception indicating a binding problem. + * + * @since 1.0 + */ +public class BindingException : RuntimeException { + + /* + * Needed because all Throwables are Serializable. + */ + private static final long serialVersionUID = -4092828452936724217L; + private Throwable cause; + + /** + * Creates a new BindingException with the given message. + * + * @param message + */ + public this(String message) { + super(message); + } + + /** + * Creates a new BindingException with the given message and cause. + * + * @param message + * @param cause + */ + public this(String message, Throwable cause) { + super(message); + this.cause = cause; + } + + public void printStackTrace(PrintStream err) { + super.printStackTrace(err); + if (cause !is null) { + err.println("caused by:"); //$NON-NLS-1$ + cause.printStackTrace(err); + } + } + + public void printStackTrace(PrintWriter err) { + super.printStackTrace(err); + if (cause !is null) { + err.println("caused by:"); //$NON-NLS-1$ + cause.printStackTrace(err); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/DataBindingContext.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/DataBindingContext.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,434 @@ +/******************************************************************************* + * Copyright (c) 2005-2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 159539 + * Brad Reynolds - bug 140644 + * Brad Reynolds - bug 159940 + * Brad Reynolds - bug 116920, 159768 + * Matthew Hall - bugs 118516, 124684, 218269 + * Boris Bokowski - bug 218269 + *******************************************************************************/ +module org.eclipse.core.databinding.DataBindingContext; + +import java.lang.all; + +import java.util.Iterator; + +import org.eclipse.core.databinding.observable.Observables; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.WritableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.internal.databinding.ValidationStatusMap; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; + +/** + * A DataBindingContext is the point of contact for the creation and management + * of {@link Binding bindings}, and aggregates validation statuses of its + * bindings, or more generally, its validation status providers. + *

+ * A DataBindingContext provides the following abilities: + *

+ *

+ *

+ * Multiple contexts can be used at any point in time. One strategy for the + * management of contexts is the aggregation of validation statuses. For example + * an IWizardPage could use a single context and the statuses + * could be aggregated to set the page status and fulfillment. Each page in the + * IWizard would have its own context instance. + *

+ * + * @since 1.0 + */ +public class DataBindingContext { + private WritableList bindings; + private WritableList validationStatusProviders; + + /** + * Unmodifiable version of {@link #bindings} for public exposure. + */ + private IObservableList unmodifiableBindings; + /** + * Unmodifiable version of {@link #validationStatusProviders} for public + * exposure. + */ + private IObservableList unmodifiableStatusProviders; + + private IObservableMap validationStatusMap; + + private Realm validationRealm; + + /** + * Creates a data binding context, using the current default realm for the + * validation observables. + * + * @see Realm + */ + public this() { + this(Realm.getDefault()); + } + + /** + * Creates a data binding context using the given realm for the validation + * observables. + * + * @param validationRealm + * the realm to be used for the validation observables + * + * @see Realm + */ + public this(Realm validationRealm) { + Assert.isNotNull(validationRealm, "Validation realm cannot be null"); //$NON-NLS-1$ + this.validationRealm = validationRealm; + + bindings = new WritableList(validationRealm); + unmodifiableBindings = Observables.unmodifiableObservableList(bindings); + + validationStatusProviders = new WritableList(validationRealm); + unmodifiableStatusProviders = Observables + .unmodifiableObservableList(validationStatusProviders); + + validationStatusMap = new ValidationStatusMap(validationRealm, bindings); + } + + /** + * Creates a {@link Binding} to synchronize the values of two + * {@link IObservableValue observable values}. During synchronization + * validation and conversion can be employed to customize the process. For + * specifics on the customization of the process see + * {@link UpdateValueStrategy}. + * + * @param targetObservableValue + * target value, commonly a UI widget + * @param modelObservableValue + * model value + * @param targetToModel + * strategy to employ when the target is the source of the change + * and the model is the destination + * @param modelToTarget + * strategy to employ when the model is the source of the change + * and the target is the destination + * @return created binding + * + * @see UpdateValueStrategy + */ + public final Binding bindValue(IObservableValue targetObservableValue, + IObservableValue modelObservableValue, + UpdateValueStrategy targetToModel, UpdateValueStrategy modelToTarget) { + UpdateValueStrategy targetToModelStrategy = targetToModel !is null ? targetToModel + : createTargetToModelUpdateValueStrategy(targetObservableValue, modelObservableValue); + UpdateValueStrategy modelToTargetStrategy = modelToTarget !is null ? modelToTarget + : createModelToTargetUpdateValueStrategy(modelObservableValue, targetObservableValue); + targetToModelStrategy.fillDefaults(targetObservableValue, modelObservableValue); + modelToTargetStrategy.fillDefaults(modelObservableValue, targetObservableValue); + ValueBinding result = new ValueBinding(targetObservableValue, + modelObservableValue, targetToModelStrategy, + modelToTargetStrategy); + result.init(this); + return result; + } + + /** + * Returns an update value strategy to be used for copying values from the + * from value to the to value. Clients may override. + * + * @param fromValue + * @param toValue + * @return a update value strategy + */ + protected UpdateValueStrategy createModelToTargetUpdateValueStrategy( + IObservableValue fromValue, IObservableValue toValue) { + return new UpdateValueStrategy(); + } + + /** + * Returns an update value strategy to be used for copying values from the + * from value to the to value. Clients may override. + * + * @param fromValue + * @param toValue + * @return a update value strategy + */ + protected UpdateValueStrategy createTargetToModelUpdateValueStrategy( + IObservableValue fromValue, IObservableValue toValue) { + return new UpdateValueStrategy(); + } + + /** + * Creates a {@link Binding} to synchronize the values of two + * {@link IObservableList observable lists}. During synchronization + * validation and conversion can be employed to customize the process. For + * specifics on the customization of the process see + * {@link UpdateListStrategy}. + * + * @param targetObservableList + * target list, commonly a list representing a list in the UI + * @param modelObservableList + * model list + * @param targetToModel + * strategy to employ when the target is the source of the change + * and the model is the destination + * @param modelToTarget + * strategy to employ when the model is the source of the change + * and the target is the destination + * @return created binding + * + * @see UpdateListStrategy + */ + public final Binding bindList(IObservableList targetObservableList, + IObservableList modelObservableList, + UpdateListStrategy targetToModel, UpdateListStrategy modelToTarget) { + UpdateListStrategy targetToModelStrategy = targetToModel !is null ? targetToModel + : createTargetToModelUpdateListStrategy(targetObservableList, + modelObservableList); + UpdateListStrategy modelToTargetStrategy = modelToTarget !is null ? modelToTarget + : createModelToTargetUpdateListStrategy(modelObservableList, + targetObservableList); + targetToModelStrategy.fillDefaults(targetObservableList, + modelObservableList); + modelToTargetStrategy.fillDefaults(modelObservableList, + targetObservableList); + ListBinding result = new ListBinding(targetObservableList, + modelObservableList, targetToModelStrategy, + modelToTargetStrategy); + result.init(this); + return result; + } + + /** + * @param modelObservableList + * @param targetObservableList + * @return an update list strategy + */ + protected UpdateListStrategy createModelToTargetUpdateListStrategy( + IObservableList modelObservableList, + IObservableList targetObservableList) { + return new UpdateListStrategy(); + } + + /** + * @param targetObservableList + * @param modelObservableList + * @return an update list strategy + */ + protected UpdateListStrategy createTargetToModelUpdateListStrategy( + IObservableList targetObservableList, + IObservableList modelObservableList) { + return new UpdateListStrategy(); + } + + /** + * Creates a {@link Binding} to synchronize the values of two + * {@link IObservableSet observable sets}. During synchronization + * validation and conversion can be employed to customize the process. For + * specifics on the customization of the process see + * {@link UpdateSetStrategy}. + * + * @param targetObservableSet + * target set, commonly a set representing a set in the UI + * @param modelObservableSet + * model set + * @param targetToModel + * strategy to employ when the target is the source of the change + * and the model is the destination + * @param modelToTarget + * strategy to employ when the model is the source of the change + * and the target is the destination + * @return created binding + * @since 1.1 + */ + public final Binding bindSet(IObservableSet targetObservableSet, + IObservableSet modelObservableSet, UpdateSetStrategy targetToModel, + UpdateSetStrategy modelToTarget) { + if (targetToModel is null) + targetToModel = createTargetToModelUpdateSetStrategy( + targetObservableSet, modelObservableSet); + if (modelToTarget is null) + modelToTarget = createModelToTargetUpdateSetStrategy( + modelObservableSet, targetObservableSet); + targetToModel.fillDefaults(targetObservableSet, modelObservableSet); + modelToTarget.fillDefaults(modelObservableSet, targetObservableSet); + SetBinding result = new SetBinding(targetObservableSet, + modelObservableSet, targetToModel, modelToTarget); + result.init(this); + return result; + } + + /** + * @param targetObservableSet + * @param modelObservableSet + * @return a default set update strategy + * @since 1.1 + */ + protected UpdateSetStrategy createTargetToModelUpdateSetStrategy( + IObservableSet targetObservableSet, + IObservableSet modelObservableSet) { + return new UpdateSetStrategy(); + } + + /** + * @param modelObservableSet + * @param targetObservableSet + * @return a default set update strategy + * @since 1.1 + */ + protected UpdateSetStrategy createModelToTargetUpdateSetStrategy( + IObservableSet modelObservableSet, + IObservableSet targetObservableSet) { + return new UpdateSetStrategy(); + } + + /** + * Disposes of this data binding context and all bindings and validation + * status providers that were added to this context. + */ + public final void dispose() { + Binding[] bindingArray = cast(Binding[]) bindings.toArray(new Binding[bindings.size()]); + for (int i = 0; i < bindingArray.length; i++) { + bindingArray[i].dispose(); + } + ValidationStatusProvider[] statusProviderArray = cast(ValidationStatusProvider[]) validationStatusProviders + .toArray(new ValidationStatusProvider[validationStatusProviders + .size()]); + for (int i = 0; i < statusProviderArray.length; i++) { + if (!statusProviderArray[i].isDisposed()) + statusProviderArray[i].dispose(); + } + } + + /** + * Returns an unmodifiable observable list with elements of type + * {@link Binding}, ordered by time of addition. + * + * @return the observable list containing all bindings + */ + public final IObservableList getBindings() { + return unmodifiableBindings; + } + + /** + * Returns an unmodifiable observable list with elements of type + * {@link ValidationStatusProvider}, ordered by time of addition. + * + * @return the observable list containing all bindings + * @since 1.1 + */ + public final IObservableList getValidationStatusProviders() { + return unmodifiableStatusProviders; + } + + /** + * Returns an observable map from bindings (type: {@link Binding}) to + * statuses (type: {@link IStatus}). The keys of the map are the bindings + * returned by {@link #getBindings()}, and the values are the current + * validaion status objects for each binding. + * + * @return the observable map from bindings to status objects. + * + * @deprecated as of 1.1, please use {@link #getValidationStatusProviders()} + */ + public final IObservableMap getValidationStatusMap() { + return validationStatusMap; + } + + /** + * Adds the given binding to this data binding context. This will also add + * the given binding to the list of validation status providers. + * + * @param binding + * The binding to add. + * @see #addValidationStatusProvidercast(ValidationStatusProvider) + * @see #getValidationStatusProviders() + */ + public void addBinding(Binding binding) { + addValidationStatusProvider(binding); + bindings.add(binding); + } + + /** + * Adds the given validation status provider to this data binding context. + * + * @param validationStatusProvider + * The validation status provider to add. + * @since 1.1 + */ + public void addValidationStatusProvider( + ValidationStatusProvider validationStatusProvider) { + validationStatusProviders.add(validationStatusProvider); + } + + /** + * Updates all model observable objects to reflect the current state of the + * target observable objects. + * + */ + public final void updateModels() { + for (Iterator it = bindings.iterator(); it.hasNext();) { + Binding binding = cast(Binding) it.next(); + binding.updateTargetToModel(); + } + } + + /** + * Updates all target observable objects to reflect the current state of the + * model observable objects. + * + */ + public final void updateTargets() { + for (Iterator it = bindings.iterator(); it.hasNext();) { + Binding binding = cast(Binding) it.next(); + binding.updateModelToTarget(); + } + } + + /** + * Removes the given binding. + * + * @param binding + * @return true if was associated with the context, + * false if not + */ + public bool removeBinding(Binding binding) { + return bindings.remove(binding) && removeValidationStatusProvider(binding); + } + + /** + * Removes the validation status provider. + * + * @param validationStatusProvider + * @return true if was associated with the context, + * false if not + * @since 1.1 + */ + public bool removeValidationStatusProvider( + ValidationStatusProvider validationStatusProvider) { + return validationStatusProviders.remove(validationStatusProvider); + } + + /** + * Returns the validation realm. + * + * @return the realm for the validation observables + * @see Realm + */ + public final Realm getValidationRealm() { + return validationRealm; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/ListBinding.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ListBinding.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.ListBinding; + +import java.lang.all; + +import java.util.Collections; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiff; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.WritableValue; +import org.eclipse.core.internal.databinding.BindingStatus; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +/** + * @since 1.0 + * + */ +public class ListBinding : Binding { + + private UpdateListStrategy targetToModel; + private UpdateListStrategy modelToTarget; + private IObservableValue validationStatusObservable; + private bool updatingTarget; + private bool updatingModel; + + private IListChangeListener targetChangeListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + if (!updatingTarget) { + doUpdate(cast(IObservableList) getTarget(), + cast(IObservableList) getModel(), event.diff, + targetToModel, false, false); + } + } + }; + private IListChangeListener modelChangeListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + if (!updatingModel) { + doUpdate(cast(IObservableList) getModel(), + cast(IObservableList) getTarget(), event.diff, + modelToTarget, false, false); + } + } + }; + + /** + * @param target + * @param model + * @param modelToTargetStrategy + * @param targetToModelStrategy + */ + public this(IObservableList target, IObservableList model, + UpdateListStrategy targetToModelStrategy, + UpdateListStrategy modelToTargetStrategy) { + super(target, model); + this.targetToModel = targetToModelStrategy; + this.modelToTarget = modelToTargetStrategy; + if ((targetToModel.getUpdatePolicy() & UpdateValueStrategy.POLICY_UPDATE) !is 0) { + target.addListChangeListener(targetChangeListener); + } else { + targetChangeListener = null; + } + if ((modelToTarget.getUpdatePolicy() & UpdateValueStrategy.POLICY_UPDATE) !is 0) { + model.addListChangeListener(modelChangeListener); + } else { + modelChangeListener = null; + } + } + + public IObservableValue getValidationStatus() { + return validationStatusObservable; + } + + protected void preInit() { + validationStatusObservable = new WritableValue(context + .getValidationRealm(), Status.OK_STATUS, IStatus.classinfo); + } + + protected void postInit() { + if (modelToTarget.getUpdatePolicy() is UpdateListStrategy.POLICY_UPDATE) { + updateModelToTarget(); + } + if (targetToModel.getUpdatePolicy() !is UpdateListStrategy.POLICY_NEVER) { + validateTargetToModel(); + } + } + + public void updateModelToTarget() { + final IObservableList modelList = cast(IObservableList) getModel(); + modelList.getRealm().exec(new class() Runnable { + public void run() { + ListDiff diff = Diffs.computeListDiff(Collections.EMPTY_LIST, + modelList); + doUpdate(modelList, cast(IObservableList) getTarget(), diff, + modelToTarget, true, true); + } + }); + } + + public void updateTargetToModel() { + final IObservableList targetList = cast(IObservableList) getTarget(); + targetList.getRealm().exec(new class() Runnable { + public void run() { + ListDiff diff = Diffs.computeListDiff(Collections.EMPTY_LIST, + targetList); + doUpdate(targetList, cast(IObservableList) getModel(), diff, + targetToModel, true, true); + } + }); + } + + public void validateModelToTarget() { + // nothing for now + } + + public void validateTargetToModel() { + // nothing for now + } + + /* + * This method may be moved to UpdateListStrategy in the future if clients + * need more control over how the two lists are kept in sync. + */ + private void doUpdate(IObservableList source, + IObservableList destination, ListDiff diff, + UpdateListStrategy updateListStrategy, + bool explicit, bool clearDestination) { + final int policy = updateListStrategy.getUpdatePolicy(); + if (policy !is UpdateListStrategy.POLICY_NEVER) { + if (policy !is UpdateListStrategy.POLICY_ON_REQUEST || explicit) { + destination.getRealm().exec(dgRunnable(( + IObservableList destination_, + ListDiff diff_, + UpdateListStrategy updateListStrategy_, + bool clearDestination_) { + if (destination_ is getTarget()) { + updatingTarget = true; + } else { + updatingModel = true; + } + MultiStatus multiStatus = BindingStatus.ok(); + + try { + if (clearDestination_) { + destination_.clear(); + } + ListDiffEntry[] diffEntries = diff_.getDifferences(); + for (int i = 0; i < diffEntries.length; i++) { + ListDiffEntry listDiffEntry = diffEntries[i]; + if (listDiffEntry.isAddition()) { + IStatus setterStatus = updateListStrategy_ + .doAdd( + destination_, + updateListStrategy_ + .convert(listDiffEntry + .getElement()), + listDiffEntry.getPosition()); + + mergeStatus(multiStatus, setterStatus); + // TODO - at this point, the two lists + // will be out of sync if an error occurred... + } else { + IStatus setterStatus = updateListStrategy_ + .doRemove(destination_, + listDiffEntry.getPosition()); + + mergeStatus(multiStatus, setterStatus); + // TODO - at this point, the two lists + // will be out of sync if an error occurred... + } + } + } finally { + validationStatusObservable.setValue(multiStatus); + + if (destination_ is getTarget()) { + updatingTarget = false; + } else { + updatingModel = false; + } + } + }, destination, diff, updateListStrategy, clearDestination)); + } + } + } + + /** + * Merges the provided newStatus into the + * multiStatus. + * + * @param multiStatus + * @param newStatus + */ + /* package */void mergeStatus(MultiStatus multiStatus, IStatus newStatus) { + if (!newStatus.isOK()) { + multiStatus.add(newStatus); + } + } + + public void dispose() { + if (targetChangeListener !is null) { + (cast(IObservableList)getTarget()).removeListChangeListener(targetChangeListener); + targetChangeListener = null; + } + if (modelChangeListener !is null) { + (cast(IObservableList)getModel()).removeListChangeListener(modelChangeListener); + modelChangeListener = null; + } + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/ObservablesManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ObservablesManager.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Bob Smith - bug 198880 + *******************************************************************************/ + +module org.eclipse.core.databinding.ObservablesManager; + +import java.lang.all; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.internal.databinding.Pair; + +/** + * An observables manager can be used for lifecycle management of + * {@link IObservable} objects. + * + * @noextend This class is not intended to be subclassed by clients. + * + * @since 1.0 + * + */ +public class ObservablesManager { + + private Set managedObservables = new HashSet(); + private Set excludedObservables = new HashSet(); + private Map contexts = new HashMap(); + + /** + * Create a new observables manager. + */ + public this() { + } + + /** + * Adds the given observable to this manager. + * + * @param observable + * the observable + */ + public void addObservable(IObservable observable) { + managedObservables.add(observable); + } + + /** + * Adds the given observable to this manager's exclusion list. The given + * observable will not be disposed of by this manager. + * + * @param observable + * the observable + */ + public void excludeObservable(IObservable observable) { + excludedObservables.add(observable); + } + + /** + * Adds the given data binding context's target and/or model observables to + * this manager. + * + * @param context + * the data binding context + * @param trackTargets + * true if the target observables of the context + * should be managed + * @param trackModels + * true if the model observables of the context + * should be managed + */ + public void addObservablesFromContext(DataBindingContext context, + bool trackTargets, bool trackModels) { + if (trackTargets || trackModels) { + contexts.put(context, new Pair(new Boolean(trackTargets), + new Boolean(trackModels))); + } + } + + /** + * Disposes of this manager and all observables that it manages. + */ + public void dispose() { + Set observables = new HashSet(); + observables.addAll(managedObservables); + for (Iterator it = contexts.keySet().iterator(); it.hasNext();) { + DataBindingContext context = cast(DataBindingContext) it.next(); + Pair trackModelsOrTargets = cast(Pair) contexts.get(context); + bool disposeTargets = (cast(Boolean) trackModelsOrTargets.a) + .booleanValue(); + bool disposeModels = (cast(Boolean) trackModelsOrTargets.b) + .booleanValue(); + for (Iterator it2 = context.getBindings().iterator(); it2.hasNext();) { + Binding binding = cast(Binding) it2.next(); + if (disposeTargets) { + observables.add(binding.getTarget()); + } + if (disposeModels) { + observables.add(binding.getModel()); + } + } + } + observables.removeAll(excludedObservables); + for (Iterator it = observables.iterator(); it.hasNext();) { + IObservable observable = cast(IObservable) it.next(); + observable.dispose(); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/SetBinding.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/SetBinding.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,225 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 124684) + * IBM Corporation - through ListBinding.java + ******************************************************************************/ + +module org.eclipse.core.databinding.SetBinding; + +import java.lang.all; + +import java.util.Collections; +import java.util.Iterator; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.WritableValue; +import org.eclipse.core.internal.databinding.BindingStatus; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +/** + * @since 1.1 + * + */ +public class SetBinding : Binding { + + private UpdateSetStrategy targetToModel; + private UpdateSetStrategy modelToTarget; + private IObservableValue validationStatusObservable; + private bool updatingTarget; + private bool updatingModel; + + private ISetChangeListener targetChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + if (!updatingTarget) { + doUpdate(cast(IObservableSet) getTarget(), + cast(IObservableSet) getModel(), event.diff, targetToModel, + false, false); + } + } + }; + + private ISetChangeListener modelChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + if (!updatingModel) { + doUpdate(cast(IObservableSet) getModel(), + cast(IObservableSet) getTarget(), event.diff, + modelToTarget, false, false); + } + } + }; + + /** + * @param target + * @param model + * @param modelToTargetStrategy + * @param targetToModelStrategy + */ + public this(IObservableSet target, IObservableSet model, + UpdateSetStrategy targetToModelStrategy, + UpdateSetStrategy modelToTargetStrategy) { + super(target, model); + this.targetToModel = targetToModelStrategy; + this.modelToTarget = modelToTargetStrategy; + if ((targetToModel.getUpdatePolicy() & UpdateSetStrategy.POLICY_UPDATE) !is 0) { + target.addSetChangeListener(targetChangeListener); + } else { + targetChangeListener = null; + } + if ((modelToTarget.getUpdatePolicy() & UpdateSetStrategy.POLICY_UPDATE) !is 0) { + model.addSetChangeListener(modelChangeListener); + } else { + modelChangeListener = null; + } + } + + public IObservableValue getValidationStatus() { + return validationStatusObservable; + } + + protected void preInit() { + validationStatusObservable = new WritableValue(context + .getValidationRealm(), Status.OK_STATUS, IStatus.classinfo); + } + + protected void postInit() { + if (modelToTarget.getUpdatePolicy() is UpdateSetStrategy.POLICY_UPDATE) { + updateModelToTarget(); + } + if (targetToModel.getUpdatePolicy() !is UpdateSetStrategy.POLICY_NEVER) { + validateTargetToModel(); + } + } + + public void updateModelToTarget() { + final IObservableSet modelSet = cast(IObservableSet) getModel(); + modelSet.getRealm().exec(new class() Runnable { + public void run() { + SetDiff diff = Diffs.computeSetDiff(Collections.EMPTY_SET, + modelSet); + doUpdate(modelSet, cast(IObservableSet) getTarget(), diff, + modelToTarget, true, true); + } + }); + } + + public void updateTargetToModel() { + final IObservableSet targetSet = cast(IObservableSet) getTarget(); + targetSet.getRealm().exec(new class() Runnable { + public void run() { + SetDiff diff = Diffs.computeSetDiff(Collections.EMPTY_SET, + targetSet); + doUpdate(targetSet, cast(IObservableSet) getModel(), diff, + targetToModel, true, true); + } + }); + } + + public void validateModelToTarget() { + // nothing for now + } + + public void validateTargetToModel() { + // nothing for now + } + + /* + * This method may be moved to UpdateSetStrategy in the future if clients + * need more control over how the two sets are kept in sync. + */ + private void doUpdate(IObservableSet source, + IObservableSet destination, SetDiff diff, + UpdateSetStrategy updateSetStrategy, bool explicit, + bool clearDestination) { + final int policy = updateSetStrategy.getUpdatePolicy(); + if (policy is UpdateSetStrategy.POLICY_NEVER) + return; + if (policy is UpdateSetStrategy.POLICY_ON_REQUEST && !explicit) + return; + destination.getRealm().exec(dgRunnable((IObservableSet destination_, SetDiff diff_, UpdateSetStrategy updateSetStrategy_, bool clearDestination_) { + if (destination_ is getTarget()) { + updatingTarget = true; + } else { + updatingModel = true; + } + MultiStatus multiStatus = BindingStatus.ok(); + + try { + if (clearDestination_) { + destination_.clear(); + } + + for (Iterator iterator = diff_.getRemovals().iterator(); iterator + .hasNext();) { + IStatus setterStatus = updateSetStrategy_.doRemove( + destination_, updateSetStrategy_.convert(iterator + .next())); + + mergeStatus(multiStatus, setterStatus); + // TODO - at this point, the two sets + // will be out of sync if an error + // occurred... + } + + for (Iterator iterator = diff_.getAdditions().iterator(); iterator + .hasNext();) { + IStatus setterStatus = updateSetStrategy_.doAdd( + destination_, updateSetStrategy_.convert(iterator + .next())); + + mergeStatus(multiStatus, setterStatus); + // TODO - at this point, the two sets + // will be out of sync if an error + // occurred... + } + } finally { + validationStatusObservable.setValue(multiStatus); + + if (destination_ is getTarget()) { + updatingTarget = false; + } else { + updatingModel = false; + } + } + }, destination, diff, updateSetStrategy, clearDestination_)); + } + + /** + * Merges the provided newStatus into the + * multiStatus. + * + * @param multiStatus + * @param newStatus + */ + /* package */void mergeStatus(MultiStatus multiStatus, IStatus newStatus) { + if (!newStatus.isOK()) { + multiStatus.add(newStatus); + } + } + + public void dispose() { + if (targetChangeListener !is null) { + (cast(IObservableSet) getTarget()) + .removeSetChangeListener(targetChangeListener); + targetChangeListener = null; + } + if (modelChangeListener !is null) { + (cast(IObservableSet) getModel()) + .removeSetChangeListener(modelChangeListener); + modelChangeListener = null; + } + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateListStrategy.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateListStrategy.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Tom Schindl - bugfix for 217940 + *******************************************************************************/ + +module org.eclipse.core.databinding.UpdateListStrategy; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.IConverter; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Customizes a {@link Binding} between two + * {@link IObservableList observable lists}. The following behaviors can be + * customized via the strategy: + * + *

+ * Conversion:
When elements are added they can be + * {@link #convertcast(Object) converted} to the destination element type. + *

+ *

+ * Automatic processing:
The processing to perform when the source + * observable changes. This behavior is configured via policies provided on + * construction of the strategy (e.g. {@link #POLICY_NEVER}, + * {@link #POLICY_ON_REQUEST}, {@link #POLICY_UPDATE}). + *

+ * + * + * @see DataBindingContext#bindList(IObservableList, IObservableList, + * UpdateListStrategy, UpdateListStrategy) + * @see IConverter + * @since 1.0 + */ +public class UpdateListStrategy : UpdateStrategy { + + /** + * Policy constant denoting that the source observable's state should not be + * tracked and that the destination observable's state should never be + * updated. + */ + public static int POLICY_NEVER = notInlined(1); + + /** + * Policy constant denoting that the source observable's state should not be + * tracked, but that conversion and updating the destination observable's + * state should be performed when explicitly requested. + */ + public static int POLICY_ON_REQUEST = notInlined(2); + + /** + * Policy constant denoting that the source observable's state should be + * tracked, and that conversion and updating the destination observable's + * state should be performed automatically on every change of the source + * observable state. + */ + public static int POLICY_UPDATE = notInlined(8); + + /** + * Helper method allowing API evolution of the above constant values. The + * compiler will not inline constant values into client code if values are + * "computed" using this helper. + * + * @param i + * an integer + * @return the same integer + */ + private static int notInlined(int i) { + return i; + } + + protected IConverter converter; + + private int updatePolicy; + + protected bool provideDefaults; + + /** + * Creates a new update list strategy for automatically updating the + * destination observable list whenever the source observable list changes. + * A default converter will be provided. The defaults can be changed by + * calling one of the setter methods. + */ + public this() { + this(true, POLICY_UPDATE); + } + + /** + * Creates a new update list strategy with a configurable update policy. A + * default converter will be provided. The defaults can be changed by + * calling one of the setter methods. + * + * @param updatePolicy + * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, or + * {@link #POLICY_UPDATE} + */ + public this(int updatePolicy) { + this(true, updatePolicy); + } + + /** + * Creates a new update list strategy with a configurable update policy. A + * default converter will be provided if provideDefaults is + * true. The defaults can be changed by calling one of the + * setter methods. + * + * @param provideDefaults + * if true, default validators and a default + * converter will be provided based on the observable list's + * type. + * @param updatePolicy + * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, or + * {@link #POLICY_UPDATE} + */ + public this(bool provideDefaults, int updatePolicy) { + this.provideDefaults = provideDefaults; + this.updatePolicy = updatePolicy; + } + + /** + * When an element is added to the destination converts the element from the + * source element type to the destination element type. + *

+ * Default implementation will use the + * {@link #setConvertercast(IConverter) converter} if one exists. If no + * converter exists no conversion occurs. + *

+ * + * @param element + * @return the converted element + */ + public Object convert(Object element) { + return converter is null ? element : converter.convert(element); + } + + /** + * + * @param source + * @param destination + */ + protected void fillDefaults(IObservableList source, + IObservableList destination) { + Object sourceType = source.getElementType(); + Object destinationType = destination.getElementType(); + if (provideDefaults && sourceType !is null && destinationType !is null) { + if (converter is null) { + setConverter(createConverter(sourceType, destinationType)); + } + } + if (converter !is null) { + if (sourceType !is null) { + checkAssignable(converter.getFromType(), sourceType, + "converter does not convert from type " + sourceType); //$NON-NLS-1$ + } + if (destinationType !is null) { + checkAssignable(converter.getToType(), destinationType, + "converter does not convert to type " + destinationType); //$NON-NLS-1$ + } + } + } + + /** + * @return the update policy + */ + public int getUpdatePolicy() { + return updatePolicy; + } + + /** + * Sets the converter to be invoked when converting added elements from the + * source element type to the destination element type. + * + * @param converter + * @return the receiver, to enable method call chaining + */ + public UpdateListStrategy setConverter(IConverter converter) { + this.converter = converter; + return this; + } + + /** + * Adds the given element at the given index to the given observable list. + * Clients may extend but must call the super implementation. + * + * @param observableList + * @param element + * @param index + * @return a status + */ + protected IStatus doAdd(IObservableList observableList, Object element, + int index) { + try { + observableList.add(index, element); + } catch (Exception ex) { + return ValidationStatus.error(BindingMessages + .getStringcast(BindingMessages.VALUEBINDING_ERROR_WHILE_SETTING_VALUE), + ex); + } + return Status.OK_STATUS; + } + + /** + * Removes the element at the given index from the given observable list. + * Clients may extend but must call the super implementation. + * + * @param observableList + * @param index + * @return a status + */ + protected IStatus doRemove(IObservableList observableList, int index) { + try { + observableList.remove(index); + } catch (Exception ex) { + return ValidationStatus.error(BindingMessages + .getStringcast(BindingMessages.VALUEBINDING_ERROR_WHILE_SETTING_VALUE), + ex); + } + return Status.OK_STATUS; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateSetStrategy.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateSetStrategy.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,232 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 124684) + * IBM Corporation - through UpdateListStrategy.java + ******************************************************************************/ + +module org.eclipse.core.databinding.UpdateSetStrategy; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.IConverter; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Customizes a {@link Binding} between two + * {@link IObservableSet observable sets}. The following behaviors can be + * customized via the strategy: + * + *

+ * Conversion:
When elements are added they can be + * {@link #convertcast(Object) converted} to the destination element type. + *

+ *

+ * Automatic processing:
The processing to perform when the source + * observable changes. This behavior is configured via policies provided on + * construction of the strategy (e.g. {@link #POLICY_NEVER}, + * {@link #POLICY_ON_REQUEST}, {@link #POLICY_UPDATE}). + *

+ * + * + * @see DataBindingContext#bindSet(IObservableSet, IObservableSet, + * UpdateSetStrategy, UpdateSetStrategy) + * @see IConverter + * @since 1.1 + */ +public class UpdateSetStrategy : UpdateStrategy { + + /** + * Policy constant denoting that the source observable's state should not be + * tracked and that the destination observable's state should never be + * updated. + */ + public final static int POLICY_NEVER = notInlined(1); + + /** + * Policy constant denoting that the source observable's state should not be + * tracked, but that conversion and updating the destination observable's + * state should be performed when explicitly requested. + */ + public final static int POLICY_ON_REQUEST = notInlined(2); + + /** + * Policy constant denoting that the source observable's state should be + * tracked, and that conversion and updating the destination observable's + * state should be performed automatically on every change of the source + * observable state. + */ + public final static int POLICY_UPDATE = notInlined(8); + + /** + * Helper method allowing API evolution of the above constant values. The + * compiler will not inline constant values into client code if values are + * "computed" using this helper. + * + * @param i + * an integer + * @return the same integer + */ + private static int notInlined(int i) { + return i; + } + + protected IConverter converter; + + private int updatePolicy; + + protected bool provideDefaults; + + /** + * Creates a new update list strategy for automatically updating the + * destination observable list whenever the source observable list changes. + * A default converter will be provided. The defaults can be changed by + * calling one of the setter methods. + */ + public this() { + this(true, POLICY_UPDATE); + } + + /** + * Creates a new update list strategy with a configurable update policy. A + * default converter will be provided. The defaults can be changed by + * calling one of the setter methods. + * + * @param updatePolicy + * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, or + * {@link #POLICY_UPDATE} + */ + public this(int updatePolicy) { + this(true, updatePolicy); + } + + /** + * Creates a new update list strategy with a configurable update policy. A + * default converter will be provided if provideDefaults is + * true. The defaults can be changed by calling one of the + * setter methods. + * + * @param provideDefaults + * if true, default validators and a default + * converter will be provided based on the observable list's + * type. + * @param updatePolicy + * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, or + * {@link #POLICY_UPDATE} + */ + public this(bool provideDefaults, int updatePolicy) { + this.provideDefaults = provideDefaults; + this.updatePolicy = updatePolicy; + } + + /** + * When an element is added to the destination converts the element from the + * source element type to the destination element type. + *

+ * Default implementation will use the + * {@link #setConvertercast(IConverter) converter} if one exists. If no + * converter exists no conversion occurs. + *

+ * + * @param element + * @return the converted element + */ + public Object convert(Object element) { + return converter is null ? element : converter.convert(element); + } + + /** + * + * @param source + * @param destination + */ + protected void fillDefaults(IObservableSet source, + IObservableSet destination) { + Object sourceType = source.getElementType(); + Object destinationType = destination.getElementType(); + if (provideDefaults && sourceType !is null && destinationType !is null) { + if (converter is null) { + setConverter(createConverter(sourceType, destinationType)); + } + } + if (converter !is null) { + if (sourceType !is null) { + checkAssignable(converter.getFromType(), sourceType, + "converter does not convert from type " + sourceType); //$NON-NLS-1$ + } + if (destinationType !is null) { + checkAssignable(converter.getToType(), destinationType, + "converter does not convert to type " + destinationType); //$NON-NLS-1$ + } + } + } + + /** + * @return the update policy + */ + public int getUpdatePolicy() { + return updatePolicy; + } + + /** + * Sets the converter to be invoked when converting added elements from the + * source element type to the destination element type. + * + * @param converter + * @return the receiver, to enable method call chaining + */ + public UpdateSetStrategy setConverter(IConverter converter) { + this.converter = converter; + return this; + } + + /** + * Adds the given element at the given index to the given observable list. + * Clients may extend but must call the super implementation. + * + * @param observableSet + * @param element + * @return a status + */ + protected IStatus doAdd(IObservableSet observableSet, Object element) { + try { + observableSet.add(element); + } catch (Exception ex) { + return ValidationStatus.error(BindingMessages + .getString("ValueBinding_ErrorWhileSettingValue"), //$NON-NLS-1$ + ex); + } + return Status.OK_STATUS; + } + + /** + * Removes the element at the given index from the given observable list. + * Clients may extend but must call the super implementation. + * + * @param observableSet + * @param element + * @return a status + */ + protected IStatus doRemove(IObservableSet observableSet, Object element) { + try { + observableSet.remove(element); + } catch (Exception ex) { + return ValidationStatus.error(BindingMessages + .getString("ValueBinding_ErrorWhileSettingValue"), //$NON-NLS-1$ + ex); + } + return Status.OK_STATUS; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,712 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matt Carter - Bug 180392 + * - Character support completed (bug 197679) + *******************************************************************************/ + +module org.eclipse.core.databinding.UpdateStrategy; + +import java.lang.all; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.databinding.conversion.IConverter; +import org.eclipse.core.databinding.conversion.NumberToStringConverter; +import org.eclipse.core.databinding.conversion.StringToNumberConverter; +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.internal.databinding.ClassLookupSupport; +import org.eclipse.core.internal.databinding.Pair; +import org.eclipse.core.internal.databinding.conversion.CharacterToStringConverter; +import org.eclipse.core.internal.databinding.conversion.IdentityConverter; +import org.eclipse.core.internal.databinding.conversion.IntegerToStringConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToBigDecimalConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter; +import org.eclipse.core.internal.databinding.conversion.ObjectToStringConverter; +import org.eclipse.core.internal.databinding.conversion.StringToByteConverter; +import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter; +import org.eclipse.core.internal.databinding.conversion.StringToShortConverter; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +import com.ibm.icu.text.NumberFormat; + +/** + * @since 1.0 + * + */ +/* package */class UpdateStrategy { + + private static final String BOOLEAN_TYPE = "java.lang.Boolean.TYPE"; //$NON-NLS-1$ + + private static final String SHORT_TYPE = "java.lang.Short.TYPE"; //$NON-NLS-1$ + + private static final String BYTE_TYPE = "java.lang.Byte.TYPE"; //$NON-NLS-1$ + + private static final String DOUBLE_TYPE = "java.lang.Double.TYPE"; //$NON-NLS-1$ + + private static final String FLOAT_TYPE = "java.lang.Float.TYPE"; //$NON-NLS-1$ + + private static final String INTEGER_TYPE = "java.lang.Integer.TYPE"; //$NON-NLS-1$ + + private static final String LONG_TYPE = "java.lang.Long.TYPE"; //$NON-NLS-1$ + + private static final String CHARACTER_TYPE = "java.lang.Character.TYPE"; //$NON-NLS-1$ + + private static Map converterMap; + + private static ClassInfo autoboxed(ClassInfo clazz) { + if (clazz is Float.TYPE) + return Float.classinfo; + else if (clazz is Double.TYPE) + return Double.classinfo; + else if (clazz is Short.TYPE) + return Short.classinfo; + else if (clazz is Integer.TYPE) + return Integer.classinfo; + else if (clazz is Long.TYPE) + return Long.classinfo; + else if (clazz is Byte.TYPE) + return Byte.classinfo; + else if (clazz is Boolean.TYPE) + return Boolean.classinfo; + else if (clazz is Character.TYPE) + return Character.classinfo; + return clazz; + } + + final protected void checkAssignable(Object toType, Object fromType, + String errorString) { + Boolean assignableFromModelToModelConverter = isAssignableFromTo( + fromType, toType); + if (assignableFromModelToModelConverter !is null + && !assignableFromModelToModelConverter.booleanValue()) { + throw new BindingException(errorString + + " Expected: " + fromType + ", actual: " + toType); //$NON-NLS-1$//$NON-NLS-2$ + } + } + + /** + * Tries to create a converter that can convert from values of type + * fromType. Returns null if no converter could be created. + * Either toType or modelDescription can be null, but not + * both. + * + * @param fromType + * @param toType + * @return an IConverter, or null if unsuccessful + */ + protected IConverter createConverter(Object fromType, Object toType) { + if (!( null !is cast(ClassInfo)fromType ) || !( null !is cast(ClassInfo)toType )) { + return new DefaultConverter(fromType, toType); + } + ClassInfo toClass = cast(ClassInfo) toType; + ClassInfo originalToClass = toClass; + if (toClass.isPrimitive()) { + toClass = autoboxed(toClass); + } + ClassInfo fromClass = cast(ClassInfo) fromType; + ClassInfo originalFromClass = fromClass; + if (fromClass.isPrimitive()) { + fromClass = autoboxed(fromClass); + } + if (!(cast(ClassInfo) toType).isPrimitive() + && toClass.isAssignableFrom(fromClass)) { + return new IdentityConverter(originalFromClass, originalToClass); + } + if ((cast(ClassInfo) fromType).isPrimitive() && (cast(ClassInfo) toType).isPrimitive() + && fromType.equals(toType)) { + return new IdentityConverter(originalFromClass, originalToClass); + } + Map converterMap = getConverterMap(); + ClassInfo[] supertypeHierarchyFlattened = ClassLookupSupport + .getTypeHierarchyFlattened(fromClass); + for (int i = 0; i < supertypeHierarchyFlattened.length; i++) { + ClassInfo currentFromClass = supertypeHierarchyFlattened[i]; + if (currentFromClass is toType) { + // converting to toType is just a widening + return new IdentityConverter(fromClass, toClass); + } + Pair key = new Pair(getKeyForClass(fromType, currentFromClass), + getKeyForClass(toType, toClass)); + Object converterOrClassname = converterMap.get(key); + if ( null !is cast(IConverter)converterOrClassname ) { + return cast(IConverter) converterOrClassname; + } else if ( null !is cast(String)converterOrClassname ) { + String classname = cast(String) converterOrClassname; + ClassInfo converterClass; + try { + converterClass = ClassInfo.forName(classname); + IConverter result = cast(IConverter) converterClass + .newInstance(); + converterMap.put(key, result); + return result; + } catch (Exception e) { + Policy + .getLog() + .log( + new Status( + IStatus.ERROR, + Policy.JFACE_DATABINDING, + 0, + "Error while instantiating default converter", e)); //$NON-NLS-1$ + } + } + } + // Since we found no converter yet, try a "downcast" converter; + // the IdentityConverter will automatically check the actual types at + // runtime. + if (fromClass.isAssignableFrom(toClass)) { + return new IdentityConverter(originalFromClass, originalToClass); + } + return new DefaultConverter(fromType, toType); + } + + private synchronized static Map getConverterMap() { + // using string-based lookup avoids loading of too many classes + if (converterMap is null) { + // NumberFormat to be shared across converters for the formatting of + // integer values + NumberFormat integerFormat = NumberFormat.getIntegerInstance(); + // NumberFormat to be shared across converters for formatting non + // integer values + NumberFormat numberFormat = NumberFormat.getNumberInstance(); + + converterMap = new HashMap(); + // Standard and Boxed Types + converterMap + .put( + new Pair("java.util.Date", "java.lang.String"), "org.eclipse.core.internal.databinding.conversion.DateToStringConverter"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Boolean"), "org.eclipse.core.internal.databinding.conversion.StringToBooleanConverter"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Byte"), StringToByteConverter.toByte(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.String", "java.util.Date"), "org.eclipse.core.internal.databinding.conversion.StringToDateConverter"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Short"), StringToShortConverter.toShort(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Character"), StringToCharacterConverter.toCharacter(false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Integer"), StringToNumberConverter.toInteger(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Double"), StringToNumberConverter.toDouble(numberFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Long"), StringToNumberConverter.toLong(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.String", "java.lang.Float"), StringToNumberConverter.toFloat(numberFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.String", "java.math.BigInteger"), StringToNumberConverter.toBigInteger(integerFormat)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.Integer", "java.lang.String"), NumberToStringConverter.fromInteger(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.Long", "java.lang.String"), NumberToStringConverter.fromLong(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.Double", "java.lang.String"), NumberToStringConverter.fromDouble(numberFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.Float", "java.lang.String"), NumberToStringConverter.fromFloat(numberFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.math.BigInteger", "java.lang.String"), NumberToStringConverter.fromBigInteger(integerFormat)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.Byte", "java.lang.String"), IntegerToStringConverter.fromByte(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.Short", "java.lang.String"), IntegerToStringConverter.fromShort(integerFormat, false)); //$NON-NLS-1$//$NON-NLS-2$ + converterMap + .put( + new Pair("java.lang.Character", "java.lang.String"), CharacterToStringConverter.fromCharacter(false)); //$NON-NLS-1$//$NON-NLS-2$ + + converterMap + .put( + new Pair("java.lang.Object", "java.lang.String"), "org.eclipse.core.internal.databinding.conversion.ObjectToStringConverter"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + + // Integer.TYPE + converterMap + .put( + new Pair("java.lang.String", INTEGER_TYPE), StringToNumberConverter.toInteger(integerFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(INTEGER_TYPE, "java.lang.Integer"), new IdentityConverter(Integer.TYPE, Integer.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(INTEGER_TYPE, "java.lang.Object"), new IdentityConverter(Integer.TYPE, Object.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(INTEGER_TYPE, "java.lang.String"), NumberToStringConverter.fromInteger(integerFormat, true)); //$NON-NLS-1$ + + // Byte.TYPE + converterMap + .put( + new Pair("java.lang.String", BYTE_TYPE), StringToByteConverter.toByte(integerFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(BYTE_TYPE, "java.lang.Byte"), new IdentityConverter(Byte.TYPE, Byte.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(BYTE_TYPE, "java.lang.String"), IntegerToStringConverter.fromByte(integerFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(BYTE_TYPE, "java.lang.Object"), new IdentityConverter(Byte.TYPE, Object.classinfo)); //$NON-NLS-1$ + + // Double.TYPE + converterMap + .put( + new Pair("java.lang.String", DOUBLE_TYPE), StringToNumberConverter.toDouble(numberFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(DOUBLE_TYPE, "java.lang.String"), NumberToStringConverter.fromDouble(numberFormat, true)); //$NON-NLS-1$ + + converterMap + .put( + new Pair(DOUBLE_TYPE, "java.lang.Double"), new IdentityConverter(Double.TYPE, Double.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(DOUBLE_TYPE, "java.lang.Object"), new IdentityConverter(Double.TYPE, Object.classinfo)); //$NON-NLS-1$ + + // Boolean.TYPE + converterMap + .put( + new Pair("java.lang.String", BOOLEAN_TYPE), "org.eclipse.core.internal.databinding.conversion.StringToBooleanPrimitiveConverter"); //$NON-NLS-1$ //$NON-NLS-2$ + converterMap + .put( + new Pair(BOOLEAN_TYPE, "java.lang.Boolean"), new IdentityConverter(Boolean.TYPE, Boolean.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(BOOLEAN_TYPE, "java.lang.String"), new ObjectToStringConvertercast(Boolean.TYPE)); //$NON-NLS-1$ + converterMap + .put( + new Pair(BOOLEAN_TYPE, "java.lang.Object"), new IdentityConverter(Boolean.TYPE, Object.classinfo)); //$NON-NLS-1$ + + // Float.TYPE + converterMap + .put( + new Pair("java.lang.String", FLOAT_TYPE), StringToNumberConverter.toFloat(numberFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(FLOAT_TYPE, "java.lang.String"), NumberToStringConverter.fromFloat(numberFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(FLOAT_TYPE, "java.lang.Float"), new IdentityConverter(Float.TYPE, Float.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(FLOAT_TYPE, "java.lang.Object"), new IdentityConverter(Float.TYPE, Object.classinfo)); //$NON-NLS-1$ + + // Short.TYPE + converterMap + .put( + new Pair("java.lang.String", SHORT_TYPE), StringToShortConverter.toShort(integerFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(SHORT_TYPE, "java.lang.Short"), new IdentityConverter(Short.TYPE, Short.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(SHORT_TYPE, "java.lang.String"), IntegerToStringConverter.fromShort(integerFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(SHORT_TYPE, "java.lang.Object"), new IdentityConverter(Short.TYPE, Object.classinfo)); //$NON-NLS-1$ + + // Long.TYPE + converterMap + .put( + new Pair("java.lang.String", LONG_TYPE), StringToNumberConverter.toLong(integerFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(LONG_TYPE, "java.lang.String"), NumberToStringConverter.fromLong(integerFormat, true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(LONG_TYPE, "java.lang.Long"), new IdentityConverter(Long.TYPE, Long.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(LONG_TYPE, "java.lang.Object"), new IdentityConverter(Long.TYPE, Object.classinfo)); //$NON-NLS-1$ + + // Character.TYPE + converterMap + .put( + new Pair("java.lang.String", CHARACTER_TYPE), StringToCharacterConverter.toCharacter(true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(CHARACTER_TYPE, "java.lang.Character"), new IdentityConverter(Character.TYPE, Character.classinfo)); //$NON-NLS-1$ + converterMap + .put( + new Pair(CHARACTER_TYPE, "java.lang.String"), CharacterToStringConverter.fromCharacter(true)); //$NON-NLS-1$ + converterMap + .put( + new Pair(CHARACTER_TYPE, "java.lang.Object"), new IdentityConverter(Character.TYPE, Object.classinfo)); //$NON-NLS-1$ + + // Miscellaneous + converterMap + .put( + new Pair( + "org.eclipse.core.runtime.IStatus", "java.lang.String"), "org.eclipse.core.internal.databinding.conversion.StatusToStringConverter"); //$NON-NLS-1$//$NON-NLS-2$ //$NON-NLS-3$ + + addNumberToByteConverters(converterMap, integerFormat, + integerClasses); + addNumberToByteConverters(converterMap, numberFormat, floatClasses); + + addNumberToShortConverters(converterMap, integerFormat, + integerClasses); + addNumberToShortConverters(converterMap, numberFormat, floatClasses); + + addNumberToIntegerConverters(converterMap, integerFormat, + integerClasses); + addNumberToIntegerConverters(converterMap, numberFormat, + floatClasses); + + addNumberToLongConverters(converterMap, integerFormat, + integerClasses); + addNumberToLongConverters(converterMap, numberFormat, floatClasses); + + addNumberToFloatConverters(converterMap, integerFormat, + integerClasses); + addNumberToFloatConverters(converterMap, numberFormat, floatClasses); + + addNumberToDoubleConverters(converterMap, integerFormat, + integerClasses); + addNumberToDoubleConverters(converterMap, numberFormat, + floatClasses); + + addNumberToBigIntegerConverters(converterMap, integerFormat, + integerClasses); + addNumberToBigIntegerConverters(converterMap, numberFormat, + floatClasses); + + addNumberToBigDecimalConverters(converterMap, integerFormat, + integerClasses); + addNumberToBigDecimalConverters(converterMap, numberFormat, + floatClasses); + } + + return converterMap; + } + + private static final ClassInfo[] integerClasses = new ClassInfo[] [ Byte.TYPE, + Byte.classinfo, Short.TYPE, Short.classinfo, Integer.TYPE, Integer.classinfo, + Long.TYPE, Long.classinfo, BigInteger.classinfo ]; + + private static final ClassInfo[] floatClasses = new ClassInfo[] [ Float.TYPE, + Float.classinfo, Double.TYPE, Double.classinfo, BigDecimal.classinfo ]; + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToByteConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(Byte.classinfo) && !fromType.equalscast(Byte.TYPE)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map + .put(new Pair(fromName, BYTE_TYPE), + new NumberToByteConverter(numberFormat, + fromType, true)); + map + .put(new Pair(fromName, Byte.classinfo.getName()), + new NumberToByteConverter(numberFormat, + fromType, false)); + } + } + } + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToShortConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(Short.classinfo) && !fromType.equalscast(Short.TYPE)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map + .put(new Pair(fromName, SHORT_TYPE), + new NumberToShortConverter(numberFormat, + fromType, true)); + map.put(new Pair(fromName, Short.classinfo.getName()), + new NumberToShortConverter(numberFormat, fromType, + false)); + } + } + } + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToIntegerConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(Integer.classinfo) + && !fromType.equalscast(Integer.TYPE)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map.put(new Pair(fromName, INTEGER_TYPE), + new NumberToIntegerConverter(numberFormat, fromType, + true)); + map.put(new Pair(fromName, Integer.classinfo.getName()), + new NumberToIntegerConverter(numberFormat, fromType, + false)); + } + } + } + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToLongConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(Long.classinfo) && !fromType.equalscast(Long.TYPE)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map + .put(new Pair(fromName, LONG_TYPE), + new NumberToLongConverter(numberFormat, + fromType, true)); + map + .put(new Pair(fromName, Long.classinfo.getName()), + new NumberToLongConverter(numberFormat, + fromType, false)); + } + } + } + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToFloatConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(Float.classinfo) && !fromType.equalscast(Float.TYPE)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map + .put(new Pair(fromName, FLOAT_TYPE), + new NumberToFloatConverter(numberFormat, + fromType, true)); + map.put(new Pair(fromName, Float.classinfo.getName()), + new NumberToFloatConverter(numberFormat, fromType, + false)); + } + } + } + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToDoubleConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(Double.classinfo) && !fromType.equalscast(Double.TYPE)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map.put(new Pair(fromName, DOUBLE_TYPE), + new NumberToDoubleConverter(numberFormat, fromType, + true)); + map.put(new Pair(fromName, Double.classinfo.getName()), + new NumberToDoubleConverter(numberFormat, fromType, + false)); + } + } + } + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToBigIntegerConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(BigInteger.classinfo)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map + .put(new Pair(fromName, BigInteger.classinfo.getName()), + new NumberToBigIntegerConverter(numberFormat, + fromType)); + } + } + } + + /** + * Registers converters to boxed and unboxed types from a list of from + * classes. + * + * @param map + * @param numberFormat + * @param fromTypes + */ + private static void addNumberToBigDecimalConverters(Map map, + NumberFormat numberFormat, ClassInfo[] fromTypes) { + for (int i = 0; i < fromTypes.length; i++) { + ClassInfo fromType = fromTypes[i]; + if (!fromType.equals(BigDecimal.classinfo)) { + String fromName = (fromType.isPrimitive()) ? getKeyForClass( + fromType, null) : fromType.getName(); + + map + .put(new Pair(fromName, BigDecimal.classinfo.getName()), + new NumberToBigDecimalConverter(numberFormat, + fromType)); + } + } + } + + private static String getKeyForClass(Object originalValue, + ClassInfo filteredValue) { + if ( null !is cast(ClassInfo)originalValue ) { + ClassInfo originalClass = cast(ClassInfo) originalValue; + if (originalClass.equalscast(Integer.TYPE)) { + return INTEGER_TYPE; + } else if (originalClass.equalscast(Byte.TYPE)) { + return BYTE_TYPE; + } else if (originalClass.equalscast(Boolean.TYPE)) { + return BOOLEAN_TYPE; + } else if (originalClass.equalscast(Double.TYPE)) { + return DOUBLE_TYPE; + } else if (originalClass.equalscast(Float.TYPE)) { + return FLOAT_TYPE; + } else if (originalClass.equalscast(Long.TYPE)) { + return LONG_TYPE; + } else if (originalClass.equalscast(Short.TYPE)) { + return SHORT_TYPE; + } + } + return filteredValue.getName(); + } + + /** + * Returns {@link Boolean#TRUE} if the from type is assignable to the to + * type, or {@link Boolean#FALSE} if it not, or null if + * unknown. + * + * @param fromType + * @param toType + * @return whether fromType is assignable to toType, or null + * if unknown + */ + protected Boolean isAssignableFromTo(Object fromType, Object toType) { + if ( null !is cast(ClassInfo )fromType && null !is cast(ClassInfo)toType ) { + ClassInfo toClass = cast(ClassInfo) toType; + if (toClass.isPrimitive()) { + toClass = autoboxed(toClass); + } + ClassInfo fromClass = cast(ClassInfo) fromType; + if (fromClass.isPrimitive()) { + fromClass = autoboxed(fromClass); + } + return toClass.isAssignableFrom(fromClass) ? Boolean.TRUE + : Boolean.FALSE; + } + return null; + } + + /* + * Default converter implementation, does not perform any conversion. + */ + protected static final class DefaultConverter : IConverter { + + private final Object toType; + + private final Object fromType; + + /** + * @param fromType + * @param toType + */ + this(Object fromType, Object toType) { + this.toType = toType; + this.fromType = fromType; + } + + public Object convert(Object fromObject) { + return fromObject; + } + + public Object getFromType() { + return fromType; + } + + public Object getToType() { + return toType; + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateValueStrategy.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateValueStrategy.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,582 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matt Carter - Character support completed (bug 197679) + * Tom Schindl - bugfix for 217940 + *******************************************************************************/ + +module org.eclipse.core.databinding.UpdateValueStrategy; + +import java.lang.all; + +import java.util.Date; +import java.util.HashMap; + +import org.eclipse.core.databinding.conversion.IConverter; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.internal.databinding.Pair; +import org.eclipse.core.internal.databinding.conversion.NumberToBigDecimalConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter; +import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter; +import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter; +import org.eclipse.core.internal.databinding.conversion.StringToDateConverter; +import org.eclipse.core.internal.databinding.validation.NumberFormatConverter; +import org.eclipse.core.internal.databinding.validation.NumberToByteValidator; +import org.eclipse.core.internal.databinding.validation.NumberToDoubleValidator; +import org.eclipse.core.internal.databinding.validation.NumberToFloatValidator; +import org.eclipse.core.internal.databinding.validation.NumberToIntegerValidator; +import org.eclipse.core.internal.databinding.validation.NumberToLongValidator; +import org.eclipse.core.internal.databinding.validation.NumberToShortValidator; +import org.eclipse.core.internal.databinding.validation.NumberToUnboundedNumberValidator; +import org.eclipse.core.internal.databinding.validation.ObjectToPrimitiveValidator; +import org.eclipse.core.internal.databinding.validation.StringToByteValidator; +import org.eclipse.core.internal.databinding.validation.StringToCharacterValidator; +import org.eclipse.core.internal.databinding.validation.StringToDateValidator; +import org.eclipse.core.internal.databinding.validation.StringToDoubleValidator; +import org.eclipse.core.internal.databinding.validation.StringToFloatValidator; +import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator; +import org.eclipse.core.internal.databinding.validation.StringToLongValidator; +import org.eclipse.core.internal.databinding.validation.StringToShortValidator; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Customizes a {@link Binding} between two + * {@link IObservableValue observable values}. The following behaviors can be + * customized via the strategy: + *
    + *
  • Validation
  • + *
  • Conversion
  • + *
  • Automatic processing
  • + *
+ *

+ * The update phases are: + *

    + *
  1. Validate after get - {@link #validateAfterGetcast(Object)}
  2. + *
  3. Conversion - {@link #convertcast(Object)}
  4. + *
  5. Validate after conversion - {@link #validateAfterConvertcast(Object)}
  6. + *
  7. Validate before set - {@link #validateBeforeSetcast(Object)}
  8. + *
  9. Value set - {@link #doSet(IObservableValue, Object)}
  10. + *
+ *

+ *

+ * Validation:
{@link IValidator Validators} validate the value at + * multiple phases in the update process. Statuses returned from validators are + * aggregated into a MultiStatus until a status of + * ERROR or CANCEL is encountered. Either of these + * statuses will abort the update process. These statuses are available as the + * {@link Binding#getValidationStatus() binding validation status}. + *

+ *

+ * Conversion:
A {@link IConverter converter} will convert the value from + * the type of the source observable into the type of the destination. The + * strategy has the ability to default converters for common scenarios. + *

+ *

+ * Automatic processing:
The processing to perform when the source + * observable changes. This behavior is configured via policies provided on + * construction of the strategy (e.g. {@link #POLICY_NEVER}, + * {@link #POLICY_CONVERT}, {@link #POLICY_ON_REQUEST}, {@link #POLICY_UPDATE}). + *

+ * + * @see DataBindingContext#bindValue(IObservableValue, IObservableValue, + * UpdateValueStrategy, UpdateValueStrategy) + * @see Binding#getValidationStatus() + * @see IValidator + * @see IConverter + * @since 1.0 + */ +public class UpdateValueStrategy : UpdateStrategy { + + /** + * Policy constant denoting that the source observable's state should not be + * tracked and that the destination observable's value should never be + * updated. + */ + public static int POLICY_NEVER = notInlined(1); + + /** + * Policy constant denoting that the source observable's state should not be + * tracked, but that validation, conversion and updating the destination + * observable's value should be performed when explicitly requested. + */ + public static int POLICY_ON_REQUEST = notInlined(2); + + /** + * Policy constant denoting that the source observable's state should be + * tracked, including validating changes except for + * {@link #validateBeforeSetcast(Object)}, but that the destination + * observable's value should only be updated on request. + */ + public static int POLICY_CONVERT = notInlined(4); + + /** + * Policy constant denoting that the source observable's state should be + * tracked, and that validation, conversion and updating the destination + * observable's value should be performed automaticlly on every change of + * the source observable value. + */ + public static int POLICY_UPDATE = notInlined(8); + + /** + * Helper method allowing API evolution of the above constant values. The + * compiler will not inline constant values into client code if values are + * "computed" using this helper. + * + * @param i + * an integer + * @return the same integer + */ + private static int notInlined(int i) { + return i; + } + + protected IValidator afterGetValidator; + protected IValidator afterConvertValidator; + protected IValidator beforeSetValidator; + protected IConverter converter; + + private int updatePolicy; + + private static ValidatorRegistry validatorRegistry = new ValidatorRegistry(); + private static HashMap validatorsByConverter = new HashMap(); + + protected bool provideDefaults; + + /** + * true if we defaulted the converter + */ + private bool defaultedConverter = false; + + /** + * Creates a new update value strategy for automatically updating the + * destination observable value whenever the source observable value + * changes. Default validators and a default converter will be provided. The + * defaults can be changed by calling one of the setter methods. + */ + public this() { + this(true, POLICY_UPDATE); + } + + /** + * Creates a new update value strategy with a configurable update policy. + * Default validators and a default converter will be provided. The defaults + * can be changed by calling one of the setter methods. + * + * @param updatePolicy + * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, + * {@link #POLICY_CONVERT}, or {@link #POLICY_UPDATE} + */ + public this(int updatePolicy) { + this(true, updatePolicy); + } + + /** + * Creates a new update value strategy with a configurable update policy. + * Default validators and a default converter will be provided if + * provideDefaults is true. The defaults can + * be changed by calling one of the setter methods. + * + * @param provideDefaults + * if true, default validators and a default + * converter will be provided based on the observable value's + * type. + * @param updatePolicy + * one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, + * {@link #POLICY_CONVERT}, or {@link #POLICY_UPDATE} + */ + public this(bool provideDefaults, int updatePolicy) { + this.provideDefaults = provideDefaults; + this.updatePolicy = updatePolicy; + } + + /** + * Converts the value from the source type to the destination type. + *

+ * Default implementation will use the + * {@link #setConvertercast(IConverter) converter} if one exists. If no + * converter exists no conversion occurs. + *

+ * + * @param value + * @return the converted value + */ + public Object convert(Object value) { + return converter is null ? value : converter.convert(value); + } + + /** + * Tries to create a validator that can validate values of type fromType. + * Returns null if no validator could be created. Either + * toType or modelDescription can be null, but not both. + * + * @param fromType + * @param toType + * @return an IValidator, or null if unsuccessful + */ + protected IValidator createValidator(Object fromType, Object toType) { + if (fromType is null || toType is null) { + return new class() IValidator { + + public IStatus validate(Object value) { + return Status.OK_STATUS; + } + }; + } + + return findValidator(fromType, toType); + } + + /** + * Fills out default values based upon the provided source + * and destination. If the strategy is to default values it + * will attempt to default a converter. If the converter can be defaulted an + * attempt is made to default the + * {@link #validateAfterGetcast(Object) after get validator}. If a validator + * cannot be defaulted it will be null. + * + * @param source + * @param destination + */ + protected void fillDefaults(IObservableValue source, + IObservableValue destination) { + Object sourceType = source.getValueType(); + Object destinationType = destination.getValueType(); + if (provideDefaults && sourceType !is null && destinationType !is null) { + if (converter is null) { + IConverter converter = createConverter(sourceType, + destinationType); + defaultedConverter = (converter !is null); + setConverter(converter); + } + + if (afterGetValidator is null) { + afterGetValidator = createValidator(sourceType, destinationType); + } + } + if (converter !is null) { + if (sourceType !is null) { + checkAssignable(converter.getFromType(), sourceType, + "converter does not convert from type " + sourceType); //$NON-NLS-1$ + } + if (destinationType !is null) { + checkAssignable(converter.getToType(), destinationType, + "converter does not convert to type " + destinationType); //$NON-NLS-1$ + } + } + } + + private IValidator findValidator(Object fromType, Object toType) { + IValidator result = null; + + // We only default the validator if we defaulted the converter since the + // two are tightly coupled. + if (defaultedConverter) { + if (String.classinfo.equals(fromType)) { + result = cast(IValidator) validatorsByConverter.get(converter); + + if (result is null) { + // TODO sring based lookup + if (Integer.classinfo.equals(toType) + || Integer.TYPE.equals(toType)) { + result = new StringToIntegerValidator( + cast(NumberFormatConverter) converter); + } else if (Long.classinfo.equals(toType) + || Long.TYPE.equals(toType)) { + result = new StringToLongValidator( + cast(NumberFormatConverter) converter); + } else if (Float.classinfo.equals(toType) + || Float.TYPE.equals(toType)) { + result = new StringToFloatValidator( + cast(NumberFormatConverter) converter); + } else if (Double.classinfo.equals(toType) + || Double.TYPE.equals(toType)) { + result = new StringToDoubleValidator( + cast(NumberFormatConverter) converter); + } else if (Byte.classinfo.equals(toType) + || Byte.TYPE.equals(toType)) { + result = new StringToByteValidator( + cast(NumberFormatConverter) converter); + } else if (Short.classinfo.equals(toType) + || Short.TYPE.equals(toType)) { + result = new StringToShortValidator( + cast(NumberFormatConverter) converter); + } else if (Character.classinfo.equals(toType) + || Character.TYPE.equals(toType) + && null !is cast(StringToCharacterConverter)converter ) { + result = new StringToCharacterValidator( + cast(StringToCharacterConverter) converter); + } else if (Date.classinfo.equals(toType) + && null !is cast(StringToDateConverter)converter ) { + result = new StringToDateValidator( + cast(StringToDateConverter) converter); + } + + if (result !is null) { + validatorsByConverter.put(converter, result); + } + } + } else if ( null !is cast(NumberToNumberConverter)converter ) { + result = cast(IValidator) validatorsByConverter.get(converter); + + if (result is null) { + if ( null !is cast(NumberToByteConverter)converter ) { + result = new NumberToByteValidator( + cast(NumberToByteConverter) converter); + } else if ( null !is cast(NumberToShortConverter)converter ) { + result = new NumberToShortValidator( + cast(NumberToShortConverter) converter); + } else if ( null !is cast(NumberToIntegerConverter)converter ) { + result = new NumberToIntegerValidator( + cast(NumberToIntegerConverter) converter); + } else if ( null !is cast(NumberToLongConverter)converter ) { + result = new NumberToLongValidator( + cast(NumberToLongConverter) converter); + } else if ( null !is cast(NumberToFloatConverter)converter ) { + result = new NumberToFloatValidator( + cast(NumberToFloatConverter) converter); + } else if ( null !is cast(NumberToDoubleConverter)converter ) { + result = new NumberToDoubleValidator( + cast(NumberToDoubleConverter) converter); + } else if ( null !is cast(NumberToBigIntegerConverter)converter + || null !is cast(NumberToBigDecimalConverter)converter ) { + result = new NumberToUnboundedNumberValidator( + cast(NumberToNumberConverter) converter); + } + } + } + + if (result is null) { + // TODO string based lookup + result = validatorRegistry.get(fromType, toType); + } + } + + return result; + } + + /** + * @return the update policy + */ + public int getUpdatePolicy() { + return updatePolicy; + } + + /** + * Sets the validator to be invoked after the source value is converted to + * the type of the destination observable. + * + * @param validator + * @return the receiver, to enable method call chaining + */ + public UpdateValueStrategy setAfterConvertValidator(IValidator validator) { + this.afterConvertValidator = validator; + return this; + } + + /** + * Sets the validator to be invoked after the source value is retrieved at + * the beginning of the synchronization process. + * + * @param validator + * @return the receiver, to enable method call chaining + */ + public UpdateValueStrategy setAfterGetValidator(IValidator validator) { + this.afterGetValidator = validator; + return this; + } + + /** + * Sets the validator to be invoked before the value is to be set on the + * destination at the end of the synchronization process. + * + * @param validator + * @return the receiver, to enable method call chaining + */ + public UpdateValueStrategy setBeforeSetValidator(IValidator validator) { + this.beforeSetValidator = validator; + return this; + } + + /** + * Sets the converter to be invoked when converting from the source type to + * the destination type. + * + * @param converter + * @return the receiver, to enable method call chaining + */ + public UpdateValueStrategy setConverter(IConverter converter) { + this.converter = converter; + return this; + } + + /** + * Validates the value after it is converted. + *

+ * Default implementation will use the + * {@link #setAfterConvertValidatorcast(IValidator) validator} if one exists. If + * one does not exist no validation will occur. + *

+ * + * @param value + * @return an ok status + */ + public IStatus validateAfterConvert(Object value) { + return afterConvertValidator is null ? Status.OK_STATUS + : afterConvertValidator.validate(value); + } + + /** + * Validates the value after it is retrieved from the source. + *

+ * Default implementation will use the + * {@link #setAfterGetValidatorcast(IValidator) validator} if one exists. If one + * does not exist no validation will occur. + *

+ * + * @param value + * @return an ok status + */ + public IStatus validateAfterGet(Object value) { + return afterGetValidator is null ? Status.OK_STATUS : afterGetValidator + .validate(value); + } + + /** + * Validates the value before it is set on the destination. + *

+ * Default implementation will use the + * {@link #setBeforeSetValidatorcast(IValidator) validator} if one exists. If + * one does not exist no validation will occur. + *

+ * + * @param value + * @return an ok status + */ + public IStatus validateBeforeSet(Object value) { + return beforeSetValidator is null ? Status.OK_STATUS + : beforeSetValidator.validate(value); + } + + /** + * Sets the current value of the given observable to the given value. + * Clients may extend but must call the super implementation. + * + * @param observableValue + * @param value + * @return status + */ + protected IStatus doSet(IObservableValue observableValue, Object value) { + try { + observableValue.setValue(value); + } catch (Exception ex) { + return ValidationStatus.error(BindingMessages + .getStringcast(BindingMessages.VALUEBINDING_ERROR_WHILE_SETTING_VALUE), + ex); + } + return Status.OK_STATUS; + } + + private static class ValidatorRegistry { + + private HashMap validators = new HashMap(); + + /** + * Adds the system-provided validators to the current validator + * registry. This is done automatically for the validator registry + * singleton. + */ + private this() { + // Standalone validators here... + associate(Integer.classinfo, Integer.TYPE, + new ObjectToPrimitiveValidatorcast(Integer.TYPE)); + associate(Byte.classinfo, Byte.TYPE, new ObjectToPrimitiveValidator( + Byte.TYPE)); + associate(Short.classinfo, Short.TYPE, new ObjectToPrimitiveValidator( + Short.TYPE)); + associate(Long.classinfo, Long.TYPE, new ObjectToPrimitiveValidator( + Long.TYPE)); + associate(Float.classinfo, Float.TYPE, new ObjectToPrimitiveValidator( + Float.TYPE)); + associate(Double.classinfo, Double.TYPE, + new ObjectToPrimitiveValidatorcast(Double.TYPE)); + associate(Boolean.classinfo, Boolean.TYPE, + new ObjectToPrimitiveValidatorcast(Boolean.TYPE)); + + associate(Object.classinfo, Integer.TYPE, + new ObjectToPrimitiveValidatorcast(Integer.TYPE)); + associate(Object.classinfo, Byte.TYPE, new ObjectToPrimitiveValidator( + Byte.TYPE)); + associate(Object.classinfo, Short.TYPE, new ObjectToPrimitiveValidator( + Short.TYPE)); + associate(Object.classinfo, Long.TYPE, new ObjectToPrimitiveValidator( + Long.TYPE)); + associate(Object.classinfo, Float.TYPE, new ObjectToPrimitiveValidator( + Float.TYPE)); + associate(Object.classinfo, Double.TYPE, + new ObjectToPrimitiveValidatorcast(Double.TYPE)); + associate(Object.classinfo, Boolean.TYPE, + new ObjectToPrimitiveValidatorcast(Boolean.TYPE)); + } + + /** + * Associate a particular validator that can validate the conversion + * (fromClass, toClass) + * + * @param fromClass + * The Class to convert from + * @param toClass + * The Class to convert to + * @param validator + * The IValidator + */ + private void associate(Object fromClass, Object toClass, + IValidator validator) { + validators.put(new Pair(fromClass, toClass), validator); + } + + /** + * Return an IValidator for a specific fromClass and toClass. + * + * @param fromClass + * The Class to convert from + * @param toClass + * The Class to convert to + * @return An appropriate IValidator + */ + private IValidator get(Object fromClass, Object toClass) { + IValidator result = cast(IValidator) validators.get(new Pair(fromClass, + toClass)); + if (result !is null) + return result; + if (fromClass !is null && toClass !is null && fromClass is toClass) { + return new class() IValidator { + public IStatus validate(Object value) { + return Status.OK_STATUS; + } + }; + } + return new class() IValidator { + public IStatus validate(Object value) { + return Status.OK_STATUS; + } + }; + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValidationStatusProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValidationStatusProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * Boris Bokowski - initial API and implementation (bug 218269) + * Matthew Hall - bug 218269 + ******************************************************************************/ + +module org.eclipse.core.databinding.ValidationStatusProvider; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.value.IObservableValue; + +/** + * A validation status provider tracks the state of zero or more target + * observables and zero or more model observables and produces a validation + * result. + * + * @since 1.1 + * + */ +public abstract class ValidationStatusProvider { + + protected bool disposed = false; + + /** + * @return an observable value containing the current validation status + */ + public abstract IObservableValue getValidationStatus(); + + /** + * Returns the list of target observables (if any) that are being tracked by + * this validation status provider. + * + * @return an observable list of target {@link IObservable}s (may be empty) + */ + public abstract IObservableList getTargets(); + + /** + * Returns the model observables (if any) that are being tracked by this + * validation status provider. + * + * @return an observable list of model {@link IObservable}s (may be empty) + */ + public abstract IObservableList getModels(); + + /** + * Disposes of this ValidationStatusProvider. Subclasses may extend, but + * must call super.dispose(). + */ + public void dispose() { + disposed = true; + } + + /** + * @return true if the binding has been disposed. false otherwise. + */ + public bool isDisposed() { + return disposed; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValueBinding.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValueBinding.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,248 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 220700 + *******************************************************************************/ + +module org.eclipse.core.databinding.ValueBinding; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.databinding.observable.value.WritableValue; +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.internal.databinding.BindingStatus; +import org.eclipse.core.internal.databinding.Util; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; + +/** + * @since 1.0 + * + */ +class ValueBinding : Binding { + private final UpdateValueStrategy targetToModel; + private final UpdateValueStrategy modelToTarget; + private WritableValue validationStatusObservable; + private IObservableValue target; + private IObservableValue model; + + private bool updatingTarget; + private bool updatingModel; + private IValueChangeListener targetChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + if (!updatingTarget && !Util.equals(event.diff.getOldValue(), event.diff.getNewValue())) { + doUpdate(target, model, targetToModel, false, false); + } + } + }; + private IValueChangeListener modelChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + if (!updatingModel && !Util.equals(event.diff.getOldValue(), event.diff.getNewValue())) { + doUpdate(model, target, modelToTarget, false, false); + } + } + }; + + /** + * @param targetObservableValue + * @param modelObservableValue + * @param targetToModel + * @param modelToTarget + */ + public this(IObservableValue targetObservableValue, + IObservableValue modelObservableValue, + UpdateValueStrategy targetToModel, UpdateValueStrategy modelToTarget) { + super(targetObservableValue, modelObservableValue); + this.target = targetObservableValue; + this.model = modelObservableValue; + this.targetToModel = targetToModel; + this.modelToTarget = modelToTarget; + if ((targetToModel.getUpdatePolicy() & (UpdateValueStrategy.POLICY_CONVERT | UpdateValueStrategy.POLICY_UPDATE)) !is 0) { + target.addValueChangeListener(targetChangeListener); + } else { + targetChangeListener = null; + } + if ((modelToTarget.getUpdatePolicy() & (UpdateValueStrategy.POLICY_CONVERT | UpdateValueStrategy.POLICY_UPDATE)) !is 0) { + model.addValueChangeListener(modelChangeListener); + } else { + modelChangeListener = null; + } + } + + protected void preInit() { + validationStatusObservable = new WritableValue(context + .getValidationRealm(), Status.OK_STATUS, IStatus.classinfo); + } + + protected void postInit() { + if (modelToTarget.getUpdatePolicy() is UpdateValueStrategy.POLICY_UPDATE) { + updateModelToTarget(); + } + if (targetToModel.getUpdatePolicy() !is UpdateValueStrategy.POLICY_NEVER) { + validateTargetToModel(); + } + } + + public IObservableValue getValidationStatus() { + return validationStatusObservable; + } + + public void updateTargetToModel() { + doUpdate(target, model, targetToModel, true, false); + } + + public void updateModelToTarget() { + doUpdate(model, target, modelToTarget, true, false); + } + + /** + * Incorporates the provided newStats into the + * multieStatus. + * + * @param multiStatus + * @param newStatus + * @return true if the update should proceed + */ + /* package */bool mergeStatus(MultiStatus multiStatus, IStatus newStatus) { + if (!newStatus.isOK()) { + multiStatus.add(newStatus); + return multiStatus.getSeverity() < IStatus.ERROR; + } + return true; + } + + /* + * This method may be moved to UpdateValueStrategy in the future if clients + * need more control over how the source value is copied to the destination + * observable. + */ + private void doUpdate(IObservableValue source, + IObservableValue destination, + UpdateValueStrategy updateValueStrategy, + bool explicit, bool validateOnly) { + + final int policy = updateValueStrategy.getUpdatePolicy(); + if (policy is UpdateValueStrategy.POLICY_NEVER) + return; + if (policy is UpdateValueStrategy.POLICY_ON_REQUEST && !explicit) + return; + + source.getRealm().exec(dgRunnable(( + IObservableValue source_, + IObservableValue destination_, + UpdateValueStrategy updateValueStrategy_, + bool explicit_, bool validateOnly_) { + bool destinationRealmReached = false; + final MultiStatus multiStatus = BindingStatus.ok(); + try { + // Get value + Object value = source_.getValue(); + + // Validate after get + IStatus status = updateValueStrategy_ + .validateAfterGet(value); + if (!mergeStatus(multiStatus, status)) + return; + + // Convert value + final Object convertedValue = updateValueStrategy_ + .convert(value); + + // Validate after convert + status = updateValueStrategy_ + .validateAfterConvert(convertedValue); + if (!mergeStatus(multiStatus, status)) + return; + if (policy is UpdateValueStrategy.POLICY_CONVERT + && !explicit_) + return; + + // Validate before set + status = updateValueStrategy_ + .validateBeforeSet(convertedValue); + if (!mergeStatus(multiStatus, status)) + return; + if (validateOnly_) + return; + + // Set value + destinationRealmReached = true; + destination_.getRealm().exec(dgRunnable(( + IObservableValue destination__, + UpdateValueStrategy updateValueStrategy__) { + if (destination__ is target) { + updatingTarget = true; + } else { + updatingModel = true; + } + try { + IStatus setterStatus = updateValueStrategy__ + .doSet(destination__, convertedValue); + + mergeStatus(multiStatus, setterStatus); + } finally { + if (destination__ is target) { + updatingTarget = false; + } else { + updatingModel = false; + } + setValidationStatus(multiStatus); + } + }, destination_, updateValueStrategy_ )); + } catch (Exception ex) { + // This check is necessary as in 3.2.2 Status + // doesn't accept a null message (bug 177264). + String message = (ex.getMessage() !is null) ? ex + .getMessage() : ""; //$NON-NLS-1$ + + mergeStatus(multiStatus, new Status(IStatus.ERROR, + Policy.JFACE_DATABINDING, IStatus.ERROR, message, + ex)); + } finally { + if (!destinationRealmReached) { + setValidationStatus(multiStatus); + } + + } + }, source, destination, updateValueStrategy, explicit, validateOnly)); + } + + public void validateModelToTarget() { + doUpdate(model, target, modelToTarget, true, true); + } + + public void validateTargetToModel() { + doUpdate(target, model, targetToModel, true, true); + } + + private void setValidationStatus( IStatus status) { + validationStatusObservable.getRealm().exec(dgRunnable((IStatus status_) { + validationStatusObservable.setValue(status_); + }, status)); + } + + public void dispose() { + if (targetChangeListener !is null) { + target.removeValueChangeListener(targetChangeListener); + targetChangeListener = null; + } + if (modelChangeListener !is null) { + model.removeValueChangeListener(modelChangeListener); + modelChangeListener = null; + } + target = null; + model = null; + super.dispose(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/Converter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/Converter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.conversion.Converter; + +import java.lang.all; + + +/** + * Abstract base class for converters. + * + * @since 1.0 + * + */ +public abstract class Converter : IConverter { + + private Object fromType; + private Object toType; + + /** + * @param fromType + * @param toType + */ + public this(Object fromType, Object toType) { + this.fromType = fromType; + this.toType = toType; + } + + public Object getFromType() { + return fromType; + } + + public Object getToType() { + return toType; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/IConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/IConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.conversion.IConverter; + +import java.lang.all; + +/** + * A one-way converter. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should subclass {@link Converter}. + * + * @since 1.0 + * + */ +public interface IConverter { + + /** + * Returns the type whose instances can be converted by this converter. The + * return type is Object rather than Class to optionally support richer type + * systems than the one provided by Java reflection. + * + * @return the type whose instances can be converted, or null if this + * converter is untyped + */ + public Object getFromType(); + + /** + * Returns the type to which this converter can convert. The return type is + * Object rather than Class to optionally support richer type systems than + * the one provided by Java reflection. + * + * @return the type to which this converter can convert, or null if this + * converter is untyped + */ + public Object getToType(); + + /** + * Returns the result of the conversion of the given object. + * + * @param fromObject + * the object to convert, of type {@link #getFromType()} + * @return the converted object, of type {@link #getToType()} + */ + public Object convert(Object fromObject); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/NumberToStringConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/NumberToStringConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,191 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.conversion.NumberToStringConverter; + +import java.lang.all; + +import java.math.BigInteger; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts a Number to a String using NumberFormat.format(...). + * This class is thread safe. + * + * @since 1.0 + */ +public class NumberToStringConverter : Converter { + private final NumberFormat numberFormat; + private final ClassInfo fromType; + private bool fromTypeIsLong; + private bool fromTypeIsDecimalType; + private bool fromTypeIsBigInteger; + + /** + * Constructs a new instance. + *

+ * Private to restrict public instantiation. + *

+ * + * @param numberFormat + * @param fromType + */ + private this(NumberFormat numberFormat, ClassInfo fromType) { + super(fromType, String.classinfo); + + this.numberFormat = numberFormat; + this.fromType = fromType; + + if (Integer.classinfo.equals(fromType) || Integer.TYPE.equals(fromType) + || Long.classinfo.equals(fromType) || Long.TYPE.equals(fromType)) { + fromTypeIsLong = true; + } else if (Float.classinfo.equals(fromType) || Float.TYPE.equals(fromType) + || Double.classinfo.equals(fromType) + || Double.TYPE.equals(fromType)) { + fromTypeIsDecimalType = true; + } else if (BigInteger.classinfo.equals(fromType)) { + fromTypeIsBigInteger = true; + } + } + + /** + * Converts the provided fromObject to a String. + * If the converter was constructed for an object type, non primitive, a + * fromObject of null will be converted to an + * empty string. + * + * @param fromObject + * value to convert. May be null if the converter + * was constructed for a non primitive type. + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + */ + public Object convert(Object fromObject) { + // Null is allowed when the type is not primitve. + if (fromObject is null && !fromType.isPrimitive()) { + return ""; //$NON-NLS-1$ + } + + Number number = cast(Number) fromObject; + String result = null; + if (fromTypeIsLong) { + synchronized (numberFormat) { + result = numberFormat.format(number.longValue()); + } + } else if (fromTypeIsDecimalType) { + synchronized (numberFormat) { + result = numberFormat.format(number.doubleValue()); + } + } else if (fromTypeIsBigInteger) { + synchronized (numberFormat) { + result = numberFormat.format(cast(BigInteger) number); + } + } + + return result; + } + + /** + * @param primitive + * true if the type is a double + * @return Double converter for the default locale + */ + public static NumberToStringConverter fromDouble(bool primitive) { + return fromDouble(NumberFormat.getNumberInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return Double converter with the provided numberFormat + */ + public static NumberToStringConverter fromDouble(NumberFormat numberFormat, + bool primitive) { + return new NumberToStringConverter(numberFormat, + (primitive) ? Double.TYPE : Double.classinfo); + } + + /** + * @param primitive + * true if the type is a long + * @return Long converter for the default locale + */ + public static NumberToStringConverter fromLong(bool primitive) { + return fromLong(NumberFormat.getIntegerInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return Long convert with the provided numberFormat + */ + public static NumberToStringConverter fromLong(NumberFormat numberFormat, + bool primitive) { + return new NumberToStringConverter(numberFormat, + (primitive) ? Long.TYPE : Long.classinfo); + } + + /** + * @param primitive + * true if the type is a float + * @return Float converter for the default locale + */ + public static NumberToStringConverter fromFloat(bool primitive) { + return fromFloat(NumberFormat.getNumberInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return Float converter with the provided numberFormat + */ + public static NumberToStringConverter fromFloat(NumberFormat numberFormat, + bool primitive) { + return new NumberToStringConverter(numberFormat, + (primitive) ? Float.TYPE : Float.classinfo); + } + + /** + * @param primitive + * true if the type is a int + * @return Integer converter for the default locale + */ + public static NumberToStringConverter fromInteger(bool primitive) { + return fromInteger(NumberFormat.getIntegerInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return Integer converter with the provided numberFormat + */ + public static NumberToStringConverter fromInteger( + NumberFormat numberFormat, bool primitive) { + return new NumberToStringConverter(numberFormat, + (primitive) ? Integer.TYPE : Integer.classinfo); + } + + /** + * @return BigInteger convert for the default locale + */ + public static NumberToStringConverter fromBigInteger() { + return fromBigInteger(NumberFormat.getIntegerInstance()); + } + + /** + * @param numberFormat + * @return BigInteger converter with the provided numberFormat + */ + public static NumberToStringConverter fromBigInteger( + NumberFormat numberFormat) { + return new NumberToStringConverter(numberFormat, BigInteger.classinfo); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/StringToNumberConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/StringToNumberConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,257 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.conversion.StringToNumberConverter; + +import java.lang.all; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser.ParseResult; +import org.eclipse.core.internal.databinding.validation.NumberFormatConverter; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts a String to a Number using NumberFormat.parse(...). + * This class is thread safe. + * + * @since 1.0 + */ +public class StringToNumberConverter : NumberFormatConverter { + private ClassInfo toType; + /** + * NumberFormat instance to use for conversion. Access must be synchronized. + */ + private NumberFormat numberFormat; + + /** + * Minimum possible value for the type. Can be null as + * BigInteger doesn't have bounds. + */ + private final Number min; + /** + * Maximum possible value for the type. Can be null as + * BigInteger doesn't have bounds. + */ + private final Number max; + + /** + * The boxed type of the toType; + */ + private final ClassInfo boxedType; + + private static final Integer MIN_INTEGER = new Integercast(Integer.MIN_VALUE); + private static final Integer MAX_INTEGER = new Integercast(Integer.MAX_VALUE); + + private static final Double MIN_DOUBLE = new Double(-Double.MAX_VALUE); + private static final Double MAX_DOUBLE = new Doublecast(Double.MAX_VALUE); + + private static final Long MIN_LONG = new Longcast(Long.MIN_VALUE); + private static final Long MAX_LONG = new Longcast(Long.MIN_VALUE); + + private static final Float MIN_FLOAT = new Float(-Float.MAX_VALUE); + private static final Float MAX_FLOAT = new Floatcast(Float.MAX_VALUE); + + /** + * @param numberFormat + * @param toType + * @param min + * minimum possible value for the type, can be null + * as BigInteger doesn't have bounds + * @param max + * maximum possible value for the type, can be null + * as BigInteger doesn't have bounds + * @param boxedType + * a convenience that allows for the checking against one type + * rather than boxed and unboxed types + */ + private this(NumberFormat numberFormat, ClassInfo toType, + Number min, Number max, ClassInfo boxedType) { + super(String.classinfo, toType, numberFormat); + + this.toType = toType; + this.numberFormat = numberFormat; + this.min = min; + this.max = max; + this.boxedType = boxedType; + } + + /** + * Converts the provided fromObject to the requested + * {@link #getToType() to type}. + * + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + * @throws IllegalArgumentException + * if the value isn't in the format required by the NumberFormat + * or the value is out of range for the + * {@link #getToType() to type}. + * @throws IllegalArgumentException + * if conversion was not possible + */ + public Object convert(Object fromObject) { + ParseResult result = StringToNumberParser.parse(fromObject, + numberFormat, toType.isPrimitive()); + + if (result.getPosition() !is null) { + // this shouldn't happen in the pipeline as validation should catch + // it but anyone can call convert so we should return a properly + // formatted message in an exception + throw new IllegalArgumentException(StringToNumberParser + .createParseErrorMessage(cast(String) fromObject, result + .getPosition())); + } else if (result.getNumber() is null) { + // if an error didn't occur and the number is null then it's a boxed + // type and null should be returned + return null; + } + + /* + * Technically the checks for ranges aren't needed here because the + * validator should have validated this already but we shouldn't assume + * this has occurred. + */ + if (Integer.classinfo.equals(boxedType)) { + if (StringToNumberParser.inIntegerRange(result.getNumber())) { + return new Integer(result.getNumber().intValue()); + } + } else if (Double.classinfo.equals(boxedType)) { + if (StringToNumberParser.inDoubleRange(result.getNumber())) { + return new Double(result.getNumber().doubleValue()); + } + } else if (Long.classinfo.equals(boxedType)) { + if (StringToNumberParser.inLongRange(result.getNumber())) { + return new Long(result.getNumber().longValue()); + } + } else if (Float.classinfo.equals(boxedType)) { + if (StringToNumberParser.inFloatRange(result.getNumber())) { + return new Float(result.getNumber().floatValue()); + } + } else if (BigInteger.classinfo.equals(boxedType)) { + return (new BigDecimal(result.getNumber().doubleValue())) + .toBigInteger(); + } + + if (min !is null && max !is null) { + throw new IllegalArgumentException(StringToNumberParser + .createOutOfRangeMessage(min, max, numberFormat)); + } + + /* + * Fail safe. I don't think this could even be thrown but throwing the + * exception is better than returning null and hiding the error. + */ + throw new IllegalArgumentException( + "Could not convert [" + fromObject + "] to type [" + toType + "]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } + + /** + * @param primitive + * true if the convert to type is an int + * @return to Integer converter for the default locale + */ + public static StringToNumberConverter toInteger(bool primitive) { + return toInteger(NumberFormat.getIntegerInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return to Integer converter with the provided numberFormat + */ + public static StringToNumberConverter toInteger(NumberFormat numberFormat, + bool primitive) { + return new StringToNumberConverter(numberFormat, + (primitive) ? Integer.TYPE : Integer.classinfo, MIN_INTEGER, + MAX_INTEGER, Integer.classinfo); + } + + /** + * @param primitive + * true if the convert to type is a double + * @return to Double converter for the default locale + */ + public static StringToNumberConverter toDouble(bool primitive) { + return toDouble(NumberFormat.getNumberInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return to Double converter with the provided numberFormat + */ + public static StringToNumberConverter toDouble(NumberFormat numberFormat, + bool primitive) { + return new StringToNumberConverter(numberFormat, + (primitive) ? Double.TYPE : Double.classinfo, MIN_DOUBLE, + MAX_DOUBLE, Double.classinfo); + } + + /** + * @param primitive + * true if the convert to type is a long + * @return to Long converter for the default locale + */ + public static StringToNumberConverter toLong(bool primitive) { + return toLong(NumberFormat.getIntegerInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return to Long converter with the provided numberFormat + */ + public static StringToNumberConverter toLong(NumberFormat numberFormat, + bool primitive) { + return new StringToNumberConverter(numberFormat, + (primitive) ? Long.TYPE : Long.classinfo, MIN_LONG, MAX_LONG, + Long.classinfo); + } + + /** + * @param primitive + * true if the convert to type is a float + * @return to Float converter for the default locale + */ + public static StringToNumberConverter toFloat(bool primitive) { + return toFloat(NumberFormat.getNumberInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return to Float converter with the provided numberFormat + */ + public static StringToNumberConverter toFloat(NumberFormat numberFormat, + bool primitive) { + return new StringToNumberConverter(numberFormat, + (primitive) ? Float.TYPE : Float.classinfo, MIN_FLOAT, MAX_FLOAT, + Float.classinfo); + } + + /** + * @return to BigInteger converter for the default locale + */ + public static StringToNumberConverter toBigInteger() { + return toBigInteger(NumberFormat.getIntegerInstance()); + } + + /** + * @param numberFormat + * @return to BigInteger converter with the provided numberFormat + */ + public static StringToNumberConverter toBigInteger(NumberFormat numberFormat) { + return new StringToNumberConverter(numberFormat, BigInteger.classinfo, + null, null, BigInteger.classinfo); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,18 @@ + + + + + + + Package-level Javadoc + + +Provides interfaces and classes for data type conversion. +

+Package Specification

+

+This package provides the IConverter interface along with classes +that implement the interface to convert between common data types. +

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/AbstractObservable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/AbstractObservable.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Matthew Hall - bug 118516 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.AbstractObservable; + +import java.lang.all; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.AssertionFailedException; + +/** + * @since 1.0 + */ +public abstract class AbstractObservable : ChangeManager , IObservable { + + /** + * @param realm + */ + public this(Realm realm) { + super(realm); + } + + public synchronized void addChangeListener(IChangeListener listener) { + addListener(ChangeEvent.TYPE, listener); + } + + public synchronized void removeChangeListener(IChangeListener listener) { + removeListener(ChangeEvent.TYPE, listener); + } + + public synchronized void addStaleListener(IStaleListener listener) { + addListener(StaleEvent.TYPE, listener); + } + + public synchronized void removeStaleListener(IStaleListener listener) { + removeListener(StaleEvent.TYPE, listener); + } + + protected void fireChange() { + checkRealm(); + fireEvent(new ChangeEvent(this)); + } + + protected void fireStale() { + checkRealm(); + fireEvent(new StaleEvent(this)); + } + + /** + * + */ + public synchronized void dispose() { + super.dispose(); + } + + /** + * Asserts that the realm is the current realm. + * + * @see Realm#isCurrent() + * @throws AssertionFailedException if the realm is not the current realm + */ + protected void checkRealm() { + Assert.isTrue(getRealm().isCurrent(), + "This operation must be run within the observable's realm"); //$NON-NLS-1$ + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.ChangeEvent; + +import java.lang.all; + +/** + * Generic change event denoting that the state of an {@link IObservable} object + * has changed. This event does not carry information about the kind of change + * that occurred. + * + * @since 1.0 + * + */ +public class ChangeEvent : ObservableEvent { + + /** + * + */ + private static final long serialVersionUID = -3241193109844979384L; + static final Object TYPE = new Object(); + + /** + * Creates a new change event object. + * + * @param source + * the observable that changed state + */ + public this(IObservable source) { + super(source); + } + + protected void dispatch(IObservablesListener listener) { + (cast(IChangeListener) listener).handleChange(this); + } + + protected Object getListenerType() { + return TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeManager.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeManager.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 118516 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.ChangeManager; + +import java.lang.all; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.ListenerList; + +/** + * Listener management implementation. Exposed to subclasses in form of + * {@link AbstractObservable} and {@link ChangeSupport}. + * + * @since 1.0 + * + */ +/* package */ class ChangeManager { + + ListenerList[] listenerLists = null; + Object listenerTypes[] = null; + private Realm realm; + + /** + * @param realm + * + */ + /* package */ this(Realm realm) { + Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$ + this.realm = realm; + } + + /** + * @param listenerType + * @param listener + */ + protected void addListener(Object listenerType, + IObservablesListener listener) { + int listenerTypeIndex = findListenerTypeIndex(listenerType); + if (listenerTypeIndex is -1) { + int length; + if (listenerTypes is null) { + length = 0; + listenerTypes = new Object[1]; + listenerLists = new ListenerList[1]; + } else { + length = listenerTypes.length; + System.arraycopy(listenerTypes, 0, + listenerTypes = new Object[length + 1], 0, length); + System + .arraycopy(listenerLists, 0, + listenerLists = new ListenerList[length + 1], + 0, length); + } + listenerTypes[length] = listenerType; + listenerLists[length] = new ListenerList(); + bool hadListeners = hasListeners(); + listenerLists[length].add(listener); + if (!hadListeners) { + this.firstListenerAdded(); + } + return; + } + ListenerList listenerList = listenerLists[listenerTypeIndex]; + bool hadListeners = true; + if (listenerList.size() is 0) { + hadListeners = hasListeners(); + } + listenerList.add(listener); + if (!hadListeners) { + firstListenerAdded(); + } + } + + /** + * @param listenerType + * @param listener + */ + protected void removeListener(Object listenerType, + IObservablesListener listener) { + int listenerTypeIndex = findListenerTypeIndex(listenerType); + if (listenerTypeIndex !is -1) { + listenerLists[listenerTypeIndex].remove(listener); + if (listenerLists[listenerTypeIndex].size() is 0) { + if (!hasListeners()) { + this.lastListenerRemoved(); + } + } + } + } + + protected bool hasListeners() { + if (listenerTypes is null) { + return false; + } + for (int i = 0; i < listenerTypes.length; i++) { + if (listenerLists[i].size() > 0) { + return true; + } + } + return false; + } + + private int findListenerTypeIndex(Object listenerType) { + if (listenerTypes !is null) { + for (int i = 0; i < listenerTypes.length; i++) { + if (listenerTypes[i] is listenerType) { + return i; + } + } + } + return -1; + } + + protected void fireEvent(ObservableEvent event) { + Object listenerType = event.getListenerType(); + int listenerTypeIndex = findListenerTypeIndex(listenerType); + if (listenerTypeIndex !is -1) { + Object[] listeners = listenerLists[listenerTypeIndex] + .getListeners(); + for (int i = 0; i < listeners.length; i++) { + event.dispatch(cast(IObservablesListener) listeners[i]); + } + } + } + + /** + * + */ + protected void firstListenerAdded() { + } + + /** + * + */ + protected void lastListenerRemoved() { + } + + /** + * + */ + public void dispose() { + listenerLists = null; + listenerTypes = null; + realm = null; + } + + /** + * @return Returns the realm. + */ + public Realm getRealm() { + return realm; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeSupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeSupport.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.ChangeSupport; + +import java.lang.all; + +/** + * @since 1.0 + * + */ +public abstract class ChangeSupport : ChangeManager { + + /** + * @param realm + */ + public this(Realm realm) { + super(realm); + } + + public void addListener(Object listenerType, + IObservablesListener listener) { + super.addListener(listenerType, listener); + } + + public void removeListener(Object listenerType, + IObservablesListener listener) { + super.removeListener(listenerType, listener); + } + + public void fireEvent(ObservableEvent event) { + super.fireEvent(event); + } + + /** + * + */ + protected abstract void firstListenerAdded(); + + /** + * + */ + protected abstract void lastListenerRemoved(); + + /** + * @param listener + */ + public void addChangeListener(IChangeListener listener) { + addListener(ChangeEvent.TYPE, listener); + } + + /** + * @param listener + */ + public void removeChangeListener(IChangeListener listener) { + removeListener(ChangeEvent.TYPE, listener); + } + + /** + * @param listener + */ + public void addStaleListener(IStaleListener listener) { + addListener(StaleEvent.TYPE, listener); + } + + /** + * @param listener + */ + public void removeStaleListener(IStaleListener listener) { + removeListener(StaleEvent.TYPE, listener); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Diffs.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Diffs.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,527 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 226216 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.Diffs; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.eclipse.core.databinding.observable.list.ListDiff; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; +import org.eclipse.core.databinding.observable.map.MapDiff; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.core.databinding.observable.value.ValueDiff; +import org.eclipse.core.internal.databinding.Util; + +/** + * @since 1.0 + * + */ +public class Diffs { + + /** + * @param oldList + * @param newList + * @return the differences between oldList and newList + */ + public static ListDiff computeListDiff(List oldList, List newList) { + List diffEntries = new ArrayList(); + createListDiffs(new ArrayList(oldList), newList, diffEntries); + ListDiff listDiff = createListDiff(cast(ListDiffEntry[]) diffEntries + .toArray(new ListDiffEntry[diffEntries.size()])); + return listDiff; + } + + /** + * adapted from EMF's ListDifferenceAnalyzer + */ + private static void createListDiffs(List oldList, List newList, + List listDiffs) { + int index = 0; + for (Iterator it = newList.iterator(); it.hasNext();) { + Object newValue = it.next(); + if (oldList.size() <= index) { + // append newValue to newList + listDiffs.add(createListDiffEntry(index, true, newValue)); + } else { + bool done; + do { + done = true; + Object oldValue = oldList.get(index); + if (oldValue is null ? newValue !is null : !oldValue.equals(newValue)) { + int oldIndexOfNewValue = listIndexOf(oldList, newValue, index); + if (oldIndexOfNewValue !is -1) { + int newIndexOfOldValue = listIndexOf(newList, oldValue, index); + if (newIndexOfOldValue is -1) { + // removing oldValue from list[index] + listDiffs.add(createListDiffEntry(index, false, oldValue)); + oldList.remove(index); + done = false; + } else if (newIndexOfOldValue > oldIndexOfNewValue) { + // moving oldValue from list[index] to [newIndexOfOldValue] + if (oldList.size() <= newIndexOfOldValue) { + // The element cannot be moved to the correct index + // now, however later iterations will insert elements + // in front of it, eventually moving it into the + // correct spot. + newIndexOfOldValue = oldList.size() - 1; + } + listDiffs.add(createListDiffEntry(index, false, oldValue)); + oldList.remove(index); + listDiffs.add(createListDiffEntry(newIndexOfOldValue, true, oldValue)); + oldList.add(newIndexOfOldValue, oldValue); + done = false; + } else { + // move newValue from list[oldIndexOfNewValue] to [index] + listDiffs.add(createListDiffEntry(oldIndexOfNewValue, false, newValue)); + oldList.remove(oldIndexOfNewValue); + listDiffs.add(createListDiffEntry(index, true, newValue)); + oldList.add(index, newValue); + } + } else { + // add newValue at list[index] + oldList.add(index, newValue); + listDiffs.add(createListDiffEntry(index, true, newValue)); + } + } + } while (!done); + } + ++index; + } + for (int i = oldList.size(); i > index;) { + // remove excess trailing elements not present in newList + listDiffs.add(createListDiffEntry(--i, false, oldList.get(i))); + } + } + + /** + * @param list + * @param object + * @param index + * @return the index, or -1 if not found + */ + private static int listIndexOf(List list, Object object, int index) { + int size = list.size(); + for (int i=index; inull -- allowing for + * null. + * + * @param left + * The left object to compare; may be null. + * @param right + * The right object to compare; may be null. + * @return true if the two objects are equivalent; + * false otherwise. + */ + public static final bool equals(Object left, Object right) { + return left is null ? right is null : ((right !is null) && left + .equals(right)); + } + + /** + * @param oldSet + * @param newSet + * @return a set diff + */ + public static SetDiff computeSetDiff(Set oldSet, Set newSet) { + Set additions = new HashSet(newSet); + additions.removeAll(oldSet); + Set removals = new HashSet(oldSet); + removals.removeAll(newSet); + return createSetDiff(additions, removals); + } + + /** + * Computes the difference between two maps. + * + * @param oldMap + * @param newMap + * @return a map diff representing the changes needed to turn oldMap into + * newMap + */ + public static MapDiff computeMapDiff(Map oldMap, Map newMap) { + // starts out with all keys from the new map, we will remove keys from + // the old map as we go + final Set addedKeys = new HashSet(newMap.keySet()); + final Set removedKeys = new HashSet(); + final Set changedKeys = new HashSet(); + final Map oldValues = new HashMap(); + final Map newValues = new HashMap(); + for (Iterator it = oldMap.entrySet().iterator(); it.hasNext();) { + Map.Entry oldEntry = cast(Entry) it.next(); + Object oldKey = oldEntry.getKey(); + if (addedKeys.remove(oldKey)) { + // potentially changed key since it is in oldMap and newMap + Object oldValue = oldEntry.getValue(); + Object newValue = newMap.get(oldKey); + if (!Util.equals(oldValue, newValue)) { + changedKeys.add(oldKey); + oldValues.put(oldKey, oldValue); + newValues.put(oldKey, newValue); + } + } else { + removedKeys.add(oldKey); + oldValues.put(oldKey, oldEntry.getValue()); + } + } + for (Iterator it = addedKeys.iterator(); it.hasNext();) { + Object newKey = it.next(); + newValues.put(newKey, newMap.get(newKey)); + } + return new class() MapDiff { + public Set getAddedKeys() { + return addedKeys; + } + + public Set getChangedKeys() { + return changedKeys; + } + + public Set getRemovedKeys() { + return removedKeys; + } + + public Object getNewValue(Object key) { + return newValues.get(key); + } + + public Object getOldValue(Object key) { + return oldValues.get(key); + } + }; + } + + /** + * @param oldValue + * @param newValue + * @return a value diff + */ + public static ValueDiff createValueDiff(Object oldValue, + Object newValue) { + return new class(oldValue, newValue) ValueDiff { + Object oldValue_, newValue_; + this(Object a, Object b){ + oldValue_=a; + newValue_=b; + } + + public Object getOldValue() { + return oldValue_; + } + + public Object getNewValue() { + return newValue_; + } + }; + } + + /** + * @param additions + * @param removals + * @return a set diff + */ + public static SetDiff createSetDiff(Set additions, Set removals) { + return new class() SetDiff { + Set unmodifiableAdditions; + Set unmodifiableRemovals; + this(){ + unmodifiableAdditions = Collections + .unmodifiableSet(additions); + unmodifiableRemovals = Collections.unmodifiableSet(removals); + } + + public Set getAdditions() { + return unmodifiableAdditions; + } + + public Set getRemovals() { + return unmodifiableRemovals; + } + }; + } + + /** + * @param difference + * @return a list diff with one differing entry + */ + public static ListDiff createListDiff(ListDiffEntry difference) { + return createListDiff([ difference ]); + } + + /** + * @param difference1 + * @param difference2 + * @return a list diff with two differing entries + */ + public static ListDiff createListDiff(ListDiffEntry difference1, + ListDiffEntry difference2) { + return createListDiff([ difference1, difference2 ]); + } + + /** + * @param differences + * @return a list diff with the given entries + */ + public static ListDiff createListDiff(ListDiffEntry[] differences) { + return new class() ListDiff { + ListDiffEntry[] differences_; + this(){ + differences_=differences; + } + public ListDiffEntry[] getDifferences() { + return differences_; + } + }; + } + + /** + * @param position + * @param isAddition + * @param element + * @return a list diff entry + */ + public static ListDiffEntry createListDiffEntry(int position, + bool isAddition, Object element) { + return new class() ListDiffEntry { + int position_; + bool isAddition_; + Object element_; + this(){ + position_=position; + isAddition_=isAddition; + element_=element; + } + + public int getPosition() { + return position_; + } + + public bool isAddition() { + return isAddition_; + } + + public Object getElement() { + return element_; + } + }; + } + + /** + * @param addedKey + * @param newValue + * @return a map diff + */ + public static MapDiff createMapDiffSingleAdd(Object addedKey, + Object newValue) { + return new class() MapDiff { + Object addedKey_, newValue_; + this(){ + addedKey_=addedKey; + newValue_=newValue; + } + + public Set getAddedKeys() { + return Collections.singleton(addedKey_); + } + + public Set getChangedKeys() { + return Collections.EMPTY_SET; + } + + public Object getNewValue(Object key) { + return newValue_; + } + + public Object getOldValue(Object key) { + return null; + } + + public Set getRemovedKeys() { + return Collections.EMPTY_SET; + } + }; + } + + /** + * @param existingKey + * @param oldValue + * @param newValue + * @return a map diff + */ + public static MapDiff createMapDiffSingleChange(Object existingKey, + Object oldValue, Object newValue) { + return new class() MapDiff { + Object existingKey_; + Object oldValue_; + Object newValue_; + this(){ + existingKey_=existingKey; + oldValue_=oldValue; + newValue_=newValue; + } + public Set getAddedKeys() { + return Collections.EMPTY_SET; + } + + public Set getChangedKeys() { + return Collections.singleton(existingKey_); + } + + public Object getNewValue(Object key) { + return newValue_; + } + + public Object getOldValue(Object key) { + return oldValue_; + } + + public Set getRemovedKeys() { + return Collections.EMPTY_SET; + } + }; + } + + /** + * @param removedKey + * @param oldValue + * @return a map diff + */ + public static MapDiff createMapDiffSingleRemove(Object removedKey, + Object oldValue) { + return new class() MapDiff { + Object removedKey_; + Object oldValue_; + this(){ + removedKey_=removedKey; + oldValue_=oldValue; + } + + public Set getAddedKeys() { + return Collections.EMPTY_SET; + } + + public Set getChangedKeys() { + return Collections.EMPTY_SET; + } + + public Object getNewValue(Object key) { + return null; + } + + public Object getOldValue(Object key) { + return oldValue_; + } + + public Set getRemovedKeys() { + return Collections.singleton(removedKey_); + } + }; + } + + /** + * @param copyOfOldMap + * @return a map diff + */ + public static MapDiff createMapDiffRemoveAll(Map copyOfOldMap) { + return new class() MapDiff { + Map copyOfOldMap_; + this(){ + copyOfOldMap_=copyOfOldMap; + } + + public Set getAddedKeys() { + return Collections.EMPTY_SET; + } + + public Set getChangedKeys() { + return Collections.EMPTY_SET; + } + + public Object getNewValue(Object key) { + return null; + } + + public Object getOldValue(Object key) { + return copyOfOldMap_.get(key); + } + + public Set getRemovedKeys() { + return copyOfOldMap_.keySet(); + } + }; + } + + /** + * @param addedKeys + * @param removedKeys + * @param changedKeys + * @param oldValues + * @param newValues + * @return a map diff + */ + public static MapDiff createMapDiff(Set addedKeys, + Set removedKeys, Set changedKeys, Map oldValues, + Map newValues) { + return new class() MapDiff { + Set addedKeys_; + Set removedKeys_; + Set changedKeys_; + Map oldValues_; + Map newValues_; + this(){ + addedKeys_=addedKeys; + removedKeys_=removedKeys; + changedKeys_=changedKeys; + oldValues_=oldValues; + newValues_=newValues; + } + + public Set getAddedKeys() { + return addedKeys_; + } + + public Set getChangedKeys() { + return changedKeys_; + } + + public Object getNewValue(Object key) { + return newValues_.get(key); + } + + public Object getOldValue(Object key) { + return oldValues_.get(key); + } + + public Set getRemovedKeys() { + return removedKeys_; + } + }; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IChangeListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IChangeListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.IChangeListener; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.map.IMapChangeListener; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; + +/** + * Listener for generic change events. Note that the change events do not carry + * information about the change, they only specify the affected observable. To + * listen for specific change events, use more specific change listeners. + * + * @see IValueChangeListener + * @see IListChangeListener + * @see ISetChangeListener + * @see IMapChangeListener + * + * @since 1.0 + */ +public interface IChangeListener : IObservablesListener { + + /** + * Handle a generic change to the given observable. The given event object + * must only be used locally in this method because it may be reused for + * other change notifications. + * + * @param event + */ + public void handleChange(ChangeEvent event); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservable.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.observable.IObservable; + +import java.lang.all; + +/** + * An object with state that allows to listen for state changes. + * + *

+ * Implementations must not manage listeners themselves, listener management + * must be delegated to a private instance of type {@link ChangeSupport} if it + * is not inherited from {@link AbstractObservable}. + *

+ * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should instead subclass one of the classes in the + * framework that implement this interface. Note that direct + * implementers of this interface outside of the framework will be + * broken in future releases when methods are added to this + * interface. + * + * @since 1.0 + * + */ +public interface IObservable { + + /** + * Returns the realm for this observable. Unless otherwise specified, + * getters and setters must be accessed from within this realm. Listeners + * will be within this realm when they receive events from this observable. + *

+ * Because observables can only be accessed from within one realm, and they + * always fire events on that realm, their state can be observed in an + * incremental way. It is always safe to call getters of an observable from + * within a change listener attached to that observable. + *

+ * + * @return the realm + */ + public Realm getRealm(); + + /** + * Adds the given change listener to the list of change listeners. Change + * listeners are notified about changes of the state of this observable in a + * generic way, without specifying the change that happened. To get the + * changed state, a change listener needs to query for the current state of + * this observable. + * + * @param listener + */ + public void addChangeListener(IChangeListener listener); + + /** + * Removes the given change listener from the list of change listeners. Has + * no effect if the given listener is not registered as a change listener. + * + * @param listener + */ + public void removeChangeListener(IChangeListener listener); + + /** + * Adds the given stale listener to the list of stale listeners. Stale + * listeners are notified when an observable object becomes stale, not when + * is becomes non-stale. + * + * @param listener + * + * @see #isStale() + */ + public void addStaleListener(IStaleListener listener); + + /** + * Removes the given stale listener from the list of stale listeners. Has no + * effect if the given listener is not registered as a stale listener. + * + * @param listener + */ + public void removeStaleListener(IStaleListener listener); + + /** + * Returns whether the state of this observable is stale and is expected to + * change soon. A non-stale observable that becomes stale will notify its + * stale listeners. A stale object that becomes non-stale does so by + * changing its state and notifying its change listeners, it does not + * notify its stale listeners about becoming non-stale. Clients that do not + * expect asynchronous changes may ignore staleness of observable objects. + * + * @return true if this observable's state is stale and will change soon. + * + * @TrackedGetter - implementers must call + * {@link ObservableTracker#getterCalledcast(IObservable)}. + */ + public bool isStale(); + + /** + * Disposes of this observable object, removing all listeners registered + * with this object, and all listeners this object might have registered on + * other objects. + */ + public void dispose(); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservableCollection.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservableCollection.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.IObservableCollection; + +import java.lang.all; + +import java.util.Collection; + +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.set.IObservableSet; + +/** + * Interface for observable collections. Only general change listeners can be + * added to an observable collection. Listeners interested in incremental + * changes have to be added using more concrete subtypes such as + * {@link IObservableList} or {@link IObservableSet}. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should instead subclass one of the classes that + * implement this interface. Note that direct implementers of this + * interface outside of the framework will be broken in future + * releases when methods are added to this interface. + *

+ * + * @since 1.0 + */ +public interface IObservableCollection : IObservable, Collection { + + /** + * @return the element type of this observable value, or null + * if this observable collection is untyped. + */ + Object getElementType(); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservablesListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservablesListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.IObservablesListener; + +import java.lang.all; + +/** + * Marker interface for all listener types in the observables framework. + * + * @noimplement This interface is not intended to be implemented by clients. + * + * @since 1.0 + */ +public interface IObservablesListener { + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObserving.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObserving.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.IObserving; + +import java.lang.all; + +/** + * + * Mixin interface for IObservables that observe other objects. + * + * @since 1.0 + * + */ +public interface IObserving { + + /** + * Returns the observed object, or null if this observing + * object does not currently observe an object. + * + * @return the observed object, or null + */ + public Object getObserved(); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IStaleListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IStaleListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.IStaleListener; + +import java.lang.all; + +/** + * Listener for staleness events. An observable object is stale if its state + * will change eventually. + * + * @since 1.0 + */ +public interface IStaleListener : IObservablesListener { + + /** + * Handle the event that the given observable object is now stale. The given + * event object must only be used locally in this method because it may be + * reused for other change notifications. + * + * @param staleEvent + */ + public void handleStale(StaleEvent staleEvent); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ObservableEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ObservableEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.ObservableEvent; + +import java.lang.all; + +import java.util.EventObject; + +/** + * Abstract event object for events fired by {@link IObservable} objects. All + * events fired by observables must be derived from this class so that the way + * of dispatching events can be improved in later versions of the framework. + * + * @since 1.0 + * + */ +public abstract class ObservableEvent : EventObject { + + /** + * Creates a new observable event. + * + * @param source + */ + public this(IObservable source) { + super(source); + } + + /** + * + */ + private static final long serialVersionUID = 7693906965267871813L; + + /** + * Returns the observable that generated this event. + * + * @return the observable that generated this event + */ + public IObservable getObservable() { + return cast(IObservable) getSource(); + } + + /** + * Dispatch this event to the given listener. Subclasses must implement this + * method by calling the appropriate type-safe event handling method on the + * given listener according to the type of this event. + * + * @param listener + * the listener that should handle the event + */ + protected abstract void dispatch(IObservablesListener listener); + + /** + * Returns a unique object used for distinguishing this event type from + * others. + * + * @return a unique object representing the concrete type of this event. + */ + protected abstract Object getListenerType(); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ObservableTracker.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ObservableTracker.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - Fix NPE, more detailed assert messages (bug 210115) + *******************************************************************************/ +module org.eclipse.core.databinding.observable.ObservableTracker; + +import java.lang.all; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.internal.databinding.IdentityWrapper; +import org.eclipse.core.runtime.Assert; + +/** + * This class makes it possible to monitor whenever an IObservable is read from. + * This can be used to automatically attach and remove listeners. How to use it: + * + *

+ * If you are implementing an IObservable, invoke getterCalled(this) whenever a + * getter is called - that is, whenever your observable is read from. You only + * need to do this once per method call. If one getter delegates to another, the + * outer getter doesn't need to call the method since the inner one will. + *

+ * + *

+ * If you want to determine what observables were used in a particular block of + * code, call runAndMonitorcast(Runnable). This will execute the given runnable and + * return the set of observables that were read from. + *

+ * + *

+ * This can be used to automatically attach listeners. For example, imagine you + * have a block of code that updates some widget by reading from a bunch of + * observables. Whenever one of those observables changes, you want to re-run + * the code and cause the widget to be refreshed. You could do this in the + * traditional manner by attaching one listener to each observable and + * re-running your widget update code whenever one of them changes, but this + * code is repetitive and requires updating the listener code whenever you + * refactor the widget updating code. + *

+ * + *

+ * Alternatively, you could use a utility class that runs the code in a + * runAndMonitor block and automatically attach listeners to any observable used + * in updating the widget. The advantage of the latter approach is that it, + * eliminates the code for attaching and detaching listeners and will always + * stay in synch with changes to the widget update logic. + *

+ * + * @since 1.0 + */ +public class ObservableTracker { + + /** + * Threadlocal storage pointing to the current Set of IObservables, or null + * if none. Note that this is actually the top of a stack. Whenever a method + * changes the current value, it remembers the old value as a local variable + * and restores the old value when the method exits. + */ + private static ThreadLocal currentChangeListener = new ThreadLocal(); + + private static ThreadLocal currentStaleListener = new ThreadLocal(); + + private static ThreadLocal currentObservableSet = new ThreadLocal(); + + /** + * Invokes the given runnable, and returns the set of IObservables that were + * read by the runnable. If the runnable calls this method recursively, the + * result will not contain IObservables that were used within the inner + * runnable. + * + * @param runnable + * runnable to execute + * @param changeListener + * listener to register with all accessed observables + * @param staleListener + * listener to register with all accessed observables, or + * null if no stale listener is to be registered + * @return an array of unique observable objects + */ + public static IObservable[] runAndMonitor(Runnable runnable, + IChangeListener changeListener, IStaleListener staleListener) { + // Remember the previous value in the listener stack + Set lastObservableSet = cast(Set) currentObservableSet.get(); + IChangeListener lastChangeListener = cast(IChangeListener) currentChangeListener + .get(); + IStaleListener lastStaleListener = cast(IStaleListener) currentStaleListener + .get(); + + Set observableSet = new HashSet(); + // Push the new listeners to the top of the stack + currentObservableSet.set(observableSet); + currentChangeListener.set(changeListener); + currentStaleListener.set(staleListener); + try { + runnable.run(); + } finally { + // Pop the new listener off the top of the stack (by restoring the + // previous listener) + currentObservableSet.set(lastObservableSet); + currentChangeListener.set(lastChangeListener); + currentStaleListener.set(lastStaleListener); + } + + int i = 0; + IObservable[] result = new IObservable[observableSet.size()]; + for (Iterator it = observableSet.iterator(); it.hasNext();) { + IdentityWrapper wrapper = cast(IdentityWrapper) it.next(); + result[i++] = cast(IObservable) wrapper.unwrap(); + } + + return result; + } + + /** + * Runs the given runnable without tracking dependencies. + * @param runnable + * + * @since 1.1 + */ + public static void runAndIgnore(Runnable runnable) { + // Remember the previous value in the listener stack + Set lastObservableSet = cast(Set) currentObservableSet.get(); + IChangeListener lastChangeListener = cast(IChangeListener) currentChangeListener + .get(); + IStaleListener lastStaleListener = cast(IStaleListener) currentStaleListener + .get(); + currentObservableSet.set(null); + currentChangeListener.set(null); + currentStaleListener.set(null); + try { + runnable.run(); + } finally { + // Pop the new listener off the top of the stack (by restoring the + // previous listener) + currentObservableSet.set(lastObservableSet); + currentChangeListener.set(lastChangeListener); + currentStaleListener.set(lastStaleListener); + } + } + + /* + * Returns the same string as the default Object.toString() implementation. + * getterCalled() uses this method IObservable.toString() to avoid infinite + * recursion and stack overflow. + */ + private static String toString(IObservable observable) { + return observable.getClass().getName() + "@" //$NON-NLS-1$ + + Integer.toHexString(System.identityHashCode(observable)); + } + + /** + * Notifies the ObservableTracker that an observable was read from. The + * JavaDoc for methods that invoke this method should include the following + * tag: "@TrackedGetter This method will notify ObservableTracker that the + * receiver has been read from". This lets callers know that they can rely + * on automatic updates from the object without explicitly attaching a + * listener. + * + * @param observable + */ + public static void getterCalled(IObservable observable) { + Realm realm = observable.getRealm(); + if (realm is null) // observable.isDisposed() would be more appropriate if it existed + Assert.isTrue(false, "Getter called on disposed observable " //$NON-NLS-1$ + + toString(observable)); + if (!realm.isCurrent()) + Assert.isTrue(false, "Getter called outside realm of observable " //$NON-NLS-1$ + + toString(observable)); + + Set lastObservableSet = cast(Set) currentObservableSet.get(); + if (lastObservableSet is null) { + return; + } + IChangeListener lastChangeListener = cast(IChangeListener) currentChangeListener + .get(); + IStaleListener lastStaleListener = cast(IStaleListener) currentStaleListener + .get(); + + bool added = false; + if (lastObservableSet !is null) { + added = lastObservableSet.add(new IdentityWrapper(observable)); + } + + // If anyone is listening for observable usage... + if (added && lastChangeListener !is null) { + observable.addChangeListener(lastChangeListener); + } + if (added && lastStaleListener !is null) { + observable.addStaleListener(lastStaleListener); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Observables.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Observables.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,521 @@ +/******************************************************************************* + * Copyright (c) 2006-2008 Cerner Corporation and others. + * 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: + * Brad Reynolds - initial API and implementation + * Matt Carter - bug 212518 (constantObservableValue) + * Matthew Hall - bugs 208332, 212518, 219909, 184830 + * Marko Topolnik - bug 184830 + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.Observables; + +import java.lang.all; + +import java.util.List; +import java.util.Set; + +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ObservableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.ObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.internal.databinding.observable.ConstantObservableValue; +import org.eclipse.core.internal.databinding.observable.EmptyObservableList; +import org.eclipse.core.internal.databinding.observable.EmptyObservableSet; +import org.eclipse.core.internal.databinding.observable.MapEntryObservableValue; +import org.eclipse.core.internal.databinding.observable.ProxyObservableList; +import org.eclipse.core.internal.databinding.observable.ProxyObservableSet; +import org.eclipse.core.internal.databinding.observable.StalenessObservableValue; +import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableList; +import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableSet; +import org.eclipse.core.internal.databinding.observable.UnmodifiableObservableValue; +import org.eclipse.core.runtime.Assert; + +/** + * Contains static methods to operate on or return + * {@link IObservable Observables}. + * + * @since 1.0 + */ +public class Observables { + /** + * Returns an unmodifiable observable value backed by the given observable + * value. + * + * @param value + * the value to wrap in an unmodifiable value + * @return an unmodifiable observable value backed by the given observable + * value + * @since 1.1 + */ + public static IObservableValue unmodifiableObservableValue( + IObservableValue value) { + Assert.isNotNull(value, "Argument 'value' cannot be null"); //$NON-NLS-1$ + return new UnmodifiableObservableValue(value); + } + + /** + * Returns an observable value with the given constant value. + * + * @param realm + * the observable's realm + * @param value + * the observable's constant value + * @param valueType + * the observable's value type + * @return an immutable observable value with the given constant value + * @since 1.1 + */ + public static IObservableValue constantObservableValue(Realm realm, + Object value, Object valueType) { + return new ConstantObservableValue(realm, value, valueType); + } + + /** + * Returns an observable value with the given constant value. + * + * @param realm + * the observable's realm + * @param value + * the observable's constant value + * @return an immutable observable value with the given constant value + * @since 1.1 + */ + public static IObservableValue constantObservableValue(Realm realm, + Object value) { + return constantObservableValue(realm, value, null); + } + + /** + * Returns an observable value with the given constant value. + * + * @param value + * the observable's constant value + * @param valueType + * the observable's value type + * @return an immutable observable value with the given constant value + * @since 1.1 + */ + public static IObservableValue constantObservableValue(Object value, + Object valueType) { + return constantObservableValue(Realm.getDefault(), value, valueType); + } + + /** + * Returns an observable value with the given constant value. + * + * @param value + * the observable's constant value + * @return an immutable observable value with the given constant value + * @since 1.1 + */ + public static IObservableValue constantObservableValue(Object value) { + return constantObservableValue(Realm.getDefault(), value, null); + } + + /** + * Returns an unmodifiable observable list backed by the given observable + * list. + * + * @param list + * the list to wrap in an unmodifiable list + * @return an unmodifiable observable list backed by the given observable + * list + */ + public static IObservableList unmodifiableObservableList( + IObservableList list) { + if (list is null) { + throw new IllegalArgumentException("List parameter cannot be null."); //$NON-NLS-1$ + } + + return new UnmodifiableObservableList(list); + } + + /** + * Returns an unmodifiable observable set backed by the given observable + * set. + * + * @param set + * the set to wrap in an unmodifiable set + * @return an unmodifiable observable set backed by the given observable set + * @since 1.1 + */ + public static IObservableSet unmodifiableObservableSet(IObservableSet set) { + if (set is null) { + throw new IllegalArgumentException("Set parameter cannot be null"); //$NON-NLS-1$ + } + + return new UnmodifiableObservableSet(set); + } + + /** + * Returns an empty observable list. The returned list continues to work + * after it has been disposed of and can be disposed of multiple times. + * + * @return an empty observable list. + */ + public static IObservableList emptyObservableList() { + return emptyObservableList(Realm.getDefault(), null); + } + + /** + * Returns an empty observable list of the given element type. The returned + * list continues to work after it has been disposed of and can be disposed + * of multiple times. + * + * @param elementType + * the element type of the returned list + * @return an empty observable list + * @since 1.1 + */ + public static IObservableList emptyObservableList(Object elementType) { + return emptyObservableList(Realm.getDefault(), elementType); + } + + /** + * Returns an empty observable list belonging to the given realm. The + * returned list continues to work after it has been disposed of and can be + * disposed of multiple times. + * + * @param realm + * the realm of the returned list + * @return an empty observable list. + */ + public static IObservableList emptyObservableList(Realm realm) { + return emptyObservableList(realm, null); + } + + /** + * Returns an empty observable list of the given element type and belonging + * to the given realm. The returned list continues to work after it has been + * disposed of and can be disposed of multiple times. + * + * @param realm + * the realm of the returned list + * @param elementType + * the element type of the returned list + * @return an empty observable list + * @since 1.1 + */ + public static IObservableList emptyObservableList(Realm realm, + Object elementType) { + return new EmptyObservableList(realm, elementType); + } + + /** + * Returns an empty observable set. The returned set continues to work after + * it has been disposed of and can be disposed of multiple times. + * + * @return an empty observable set. + */ + public static IObservableSet emptyObservableSet() { + return emptyObservableSet(Realm.getDefault(), null); + } + + /** + * Returns an empty observable set of the given element type. The returned + * set continues to work after it has been disposed of and can be disposed + * of multiple times. + * + * @param elementType + * the element type of the returned set + * @return an empty observable set + * @since 1.1 + */ + public static IObservableSet emptyObservableSet(Object elementType) { + return emptyObservableSet(Realm.getDefault(), elementType); + } + + /** + * Returns an empty observable set belonging to the given realm. The + * returned set continues to work after it has been disposed of and can be + * disposed of multiple times. + * + * @param realm + * the realm of the returned set + * @return an empty observable set. + */ + public static IObservableSet emptyObservableSet(Realm realm) { + return emptyObservableSet(realm, null); + } + + /** + * Returns an empty observable set of the given element type and belonging + * to the given realm. The returned set continues to work after it has been + * disposed of and can be disposed of multiple times. + * + * @param realm + * the realm of the returned set + * @param elementType + * the element type of the returned set + * @return an empty observable set + * @since 1.1 + */ + public static IObservableSet emptyObservableSet(Realm realm, + Object elementType) { + return new EmptyObservableSet(realm, elementType); + } + + /** + * Returns an observable set backed by the given set. + * + * @param set + * the set to wrap in an IObservableSet + * @return an observable set backed by the given set + */ + public static IObservableSet staticObservableSet(Set set) { + return staticObservableSet(Realm.getDefault(), set, Object.classinfo); + } + + /** + * Returns an observable set of the given element type, backed by the given + * set. + * + * @param set + * the set to wrap in an IObservableSet + * @param elementType + * the element type of the returned set + * @return Returns an observable set backed by the given unchanging set + * @since 1.1 + */ + public static IObservableSet staticObservableSet(Set set, Object elementType) { + return staticObservableSet(Realm.getDefault(), set, elementType); + } + + /** + * Returns an observable set belonging to the given realm, backed by the + * given set. + * + * @param realm + * the realm of the returned set + * @param set + * the set to wrap in an IObservableSet + * @return an observable set backed by the given unchanging set + */ + public static IObservableSet staticObservableSet(Realm realm, Set set) { + return staticObservableSet(realm, set, Object.classinfo); + } + + /** + * Returns an observable set of the given element type and belonging to the + * given realm, backed by the given set. + * + * @param realm + * the realm of the returned set + * @param set + * the set to wrap in an IObservableSet + * @param elementType + * the element type of the returned set + * @return an observable set backed by the given set + * @since 1.1 + */ + public static IObservableSet staticObservableSet(Realm realm, Set set, + Object elementType) { + return new class(realm, set, elementType) ObservableSet { + public void addChangeListener(IChangeListener listener) { + } + + public void addStaleListener(IStaleListener listener) { + } + + public void addSetChangeListener(ISetChangeListener listener) { + } + }; + } + + /** + * Returns an observable set that contains the same elements as the given + * set, and fires the same events as the given set, but can be disposed of + * without disposing of the wrapped set. + * + * @param target + * the set to wrap + * @return a disposable proxy for the given observable set + */ + public static IObservableSet proxyObservableSet(IObservableSet target) { + return new ProxyObservableSet(target); + } + + /** + * Returns an observable list that contains the same elements as the given + * list, and fires the same events as the given list, but can be disposed of + * without disposing of the wrapped list. + * + * @param target + * the list to wrap + * @return a disposable proxy for the given observable list + * @since 1.1 + */ + public static IObservableList proxyObservableList(IObservableList target) { + return new ProxyObservableList(target); + } + + /** + * Returns an observable list backed by the given list. + * + * @param list + * the list to wrap in an IObservableList + * @return an observable list backed by the given unchanging list + */ + public static IObservableList staticObservableList(List list) { + return staticObservableList(Realm.getDefault(), list, Object.classinfo); + } + + /** + * Returns an observable list of the given element type, backed by the given + * list. + * + * @param list + * the list to wrap in an IObservableList + * @param elementType + * the element type of the returned list + * @return an observable list backed by the given unchanging list + * @since 1.1 + */ + public static IObservableList staticObservableList(List list, + Object elementType) { + return staticObservableList(Realm.getDefault(), list, elementType); + } + + /** + * Returns an observable list belonging to the given realm, backed by the + * given list. + * + * @param realm + * the realm of the returned list + * @param list + * the list to wrap in an IObservableList + * @return an observable list backed by the given unchanging list + */ + public static IObservableList staticObservableList(Realm realm, List list) { + return staticObservableList(realm, list, Object.classinfo); + } + + /** + * Returns an observable list of the given element type and belonging to the + * given realm, backed by the given list. + * + * @param realm + * the realm of the returned list + * @param list + * the list to wrap in an IObservableList + * @param elementType + * the element type of the returned list + * @return an observable list backed by the given unchanging list + * @since 1.1 + */ + public static IObservableList staticObservableList(Realm realm, List list, + Object elementType) { + return new class(realm, list, elementType) ObservableList { + public void addChangeListener(IChangeListener listener) { + } + + public void addStaleListener(IStaleListener listener) { + } + + public void addListChangeListener(IListChangeListener listener) { + } + }; + } + + /** + * Returns an observable value of type Boolean.TYPE which + * tracks whether the given observable is stale. + * + * @param observable + * the observable to track + * @return an observable value which tracks whether the given observable is + * stale + * + * @since 1.1 + */ + public static IObservableValue observeStale(IObservable observable) { + return new StalenessObservableValue(observable); + } + + /** + * Returns an observable value that tracks changes to the value of an + * observable map's entry specified by its key. + *

+ * The state where the key does not exist in the map is equivalent to the + * state where the key exists and its value is null. The + * transition between these two states is not considered a value change and + * no event is fired. + * + * @param map + * the observable map whose entry will be tracked. + * @param key + * the key identifying the map entry to track. + * @param valueType + * the type of the value. May be null, meaning + * the value is untyped. + * @return an observable value that tracks the value associated with the + * specified key in the given map + * @since 1.1 + */ + public static IObservableValue observeMapEntry(IObservableMap map, + Object key, Object valueType) { + return new MapEntryObservableValue(map, key, valueType); + } + + /** + * Returns a factory for creating obervable values tracking the value of the + * {@link IObservableMap observable map} entry identified by a particular + * key. + * + * @param map + * the observable map whose entry will be tracked. + * @param valueType + * the type of the value. May be null, meaning + * the value is untyped. + * @return a factory for creating observable values tracking the value of + * the observable map entry identified by a particular key object. + * @since 1.1 + */ + public static IObservableFactory mapEntryValueFactory( + IObservableMap map, Object valueType) { + return new class() IObservableFactory { + IObservableMap map_; + Object valueType_; + this(IObservableMap a, Object b){ + map_=a; valueType_=b; + } + public IObservable createObservable(Object key) { + return observeMapEntry(map_, key, valueType_); + } + }; + } + + /** + * Helper method for MasterDetailObservables.detailValue(master, + * mapEntryValueFactory(map, valueType), valueType). + * + * @param map + * the observable map whose entry will be tracked. + * @param master + * the observable value that identifies which map entry to track. + * @param valueType + * the type of the value. May be null, meaning + * the value is untyped. + * @return an observable value tracking the current value of the specified + * key in the given map an observable value that tracks the current + * value of the named property for the current value of the master + * observable value + * @since 1.1 + */ + public static IObservableValue observeDetailMapEntry(IObservableMap map, + IObservableValue master, Object valueType) { + return MasterDetailObservables.detailValue(master, + mapEntryValueFactory(map, valueType), valueType); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Realm.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Realm.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,297 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 168153 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.Realm; + +import java.lang.all; + +import org.eclipse.core.databinding.Binding; +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.internal.databinding.Queue; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; + +/** + * A realm defines a context from which objects implementing {@link IObservable} + * must be accessed, and on which these objects will notify their listeners. To + * bridge between observables from different realms, subclasses of + * {@link Binding} can be used. + *

+ * A block of code is said to be executing within a realm if calling + * {@link #isCurrent()} from that block returns true. Code reached by calling + * methods from that block will execute within the same realm, with the + * exception of methods on this class that can be used to execute code within a + * specific realm. Clients can use {@link #syncExeccast(Runnable)}, + * {@link #asyncExeccast(Runnable)}, or {@link #execcast(Runnable)} to execute a + * runnable within this realm. Note that using {@link #syncExeccast(Runnable)} can + * lead to deadlocks and should be avoided if the current thread holds any + * locks. + *

+ *

+ * It is instructive to think about possible implementations of Realm: It can be + * based on executing on a designated thread such as a UI thread, or based on + * holding a lock. In the former case, calling syncExec on a realm that is not + * the current realm will execute the given runnable on a different thread (the + * designated thread). In the latter case, calling syncExec may execute the + * given runnable on the calling thread, but calling + * {@link #asyncExeccast(Runnable)} will execute the given runnable on a different + * thread. Therefore, no assumptions can be made about the thread that will + * execute arguments to {@link #asyncExeccast(Runnable)}, + * {@link #syncExeccast(Runnable)}, or {@link #execcast(Runnable)}. + *

+ *

+ * It is possible that a block of code is executing within more than one realm. + * This can happen for implementations of Realm that are based on holding a lock + * but don't use a separate thread to run runnables given to + * {@link #syncExeccast(Runnable)}. Realm implementations of this kind should be + * appropriately documented because it increases the opportunity for deadlock. + *

+ *

+ * Some implementations of {@link IObservable} provide constructors which do not + * take a Realm argument and are specified to create the observable instance + * with the current default realm. The default realm can be set for the + * currently executing thread by using {@link #runWithDefault(Realm, Runnable)}. + * Note that the default realm does not have to be the current realm. + *

+ *

+ * Subclasses must override at least one of asyncExec()/syncExec(). For realms + * based on a designated thread, it may be easier to implement asyncExec and + * keep the default implementation of syncExec. For realms based on holding a + * lock, it may be easier to implement syncExec and keep the default + * implementation of asyncExec. + *

+ * + * @since 1.0 + * + * @see IObservable + */ +public abstract class Realm { + + private static ThreadLocal defaultRealm = new ThreadLocal(); + + /** + * Returns the default realm for the calling thread, or null + * if no default realm has been set. + * + * @return the default realm, or null + */ + public static Realm getDefault() { + return cast(Realm) defaultRealm.get(); + } + + /** + * Sets the default realm for the calling thread, returning the current + * default thread. This method is inherently unsafe, it is recommended to + * use {@link #runWithDefault(Realm, Runnable)} instead. This method is + * exposed to subclasses to facilitate testing. + * + * @param realm + * the new default realm, or null + * @return the previous default realm, or null + */ + protected static Realm setDefault(Realm realm) { + Realm oldValue = getDefault(); + defaultRealm.set(realm); + return oldValue; + } + + /** + * @return true if the caller is executing in this realm. This method must + * not have side-effects (such as, for example, implicitly placing + * the caller in this realm). + */ + abstract public bool isCurrent(); + + private Thread workerThread; + + Queue workQueue = new Queue(); + + /** + * Runs the given runnable. If an exception occurs within the runnable, it + * is logged and not re-thrown. If the runnable implements + * {@link ISafeRunnable}, the exception is passed to its + * handleException method. + * + * @param runnable + */ + protected static void safeRun(Runnable runnable) { + ISafeRunnable safeRunnable; + if ( null !is cast(ISafeRunnable)runnable ) { + safeRunnable = cast(ISafeRunnable) runnable; + } else { + safeRunnable = new class(runnable) ISafeRunnable { + Runnable runnable_; + this(Runnable r){runnable_=r;} + public void handleException(Throwable exception) { + Policy + .getLog() + .log( + new Status( + IStatus.ERROR, + Policy.JFACE_DATABINDING, + IStatus.OK, + "Unhandled exception: " + exception.getMessage(), exception)); //$NON-NLS-1$ + } + public void run() { + runnable_.run(); + } + }; + } + SafeRunner.run(safeRunnable); + } + + /** + * Causes the run() method of the runnable to be invoked from + * within this realm. If the caller is executing in this realm, the + * runnable's run method is invoked directly, otherwise it is run at the + * next reasonable opportunity using asyncExec. + *

+ * If the given runnable is an instance of {@link ISafeRunnable}, its + * exception handler method will be called if any exceptions occur while + * running it. Otherwise, the exception will be logged. + *

+ * + * @param runnable + */ + public void exec(Runnable runnable) { + if (isCurrent()) { + safeRun(runnable); + } else { + asyncExec(runnable); + } + } + + /** + * Causes the run() method of the runnable to be invoked from + * within this realm at the next reasonable opportunity. The caller of this + * method continues to run in parallel, and is not notified when the + * runnable has completed. + *

+ * If the given runnable is an instance of {@link ISafeRunnable}, its + * exception handler method will be called if any exceptions occur while + * running it. Otherwise, the exception will be logged. + *

+ *

+ * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable. + *

+ * + * @param runnable + */ + public void asyncExec(Runnable runnable) { + synchronized (workQueue) { + ensureWorkerThreadIsRunning(); + workQueue.enqueue(runnable); + workQueue.notifyAll(); + } + } + + /** + * + */ + private void ensureWorkerThreadIsRunning() { + if (workerThread is null) { + workerThread = new class() Thread { + public void run() { + try { + while (true) { + Runnable work = null; + synchronized (workQueue) { + while (workQueue.isEmpty()) { + workQueue.wait(); + } + work = cast(Runnable) workQueue.dequeue(); + } + syncExec(work); + } + } catch (InterruptedException e) { + // exit + } + } + }; + workerThread.start(); + } + } + + /** + * Causes the run() method of the runnable to be invoked from + * within this realm at the next reasonable opportunity. This method is + * blocking the caller until the runnable completes. + *

+ * If the given runnable is an instance of {@link ISafeRunnable}, its + * exception handler method will be called if any exceptions occur while + * running it. Otherwise, the exception will be logged. + *

+ *

+ * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable. + *

+ *

+ * Note: This class is not meant to be called by clients and therefore has + * only protected access. + *

+ * + * @param runnable + */ + protected void syncExec(Runnable runnable) { + SyncRunnable syncRunnable = new SyncRunnable(runnable); + asyncExec(syncRunnable); + synchronized (syncRunnable) { + while (!syncRunnable.hasRun) { + try { + syncRunnable.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + } + + static class SyncRunnable : Runnable { + bool hasRun = false; + + private Runnable runnable; + + this(Runnable runnable) { + this.runnable = runnable; + } + + public void run() { + try { + safeRun(runnable); + } finally { + synchronized (this) { + hasRun = true; + this.notifyAll(); + } + } + } + } + + /** + * Sets the provided realm as the default for the duration of + * {@link Runnable#run()} and resets the previous realm after completion. + * Note that this will not set the given realm as the current realm. + * + * @param realm + * @param runnable + */ + public static void runWithDefault(Realm realm, Runnable runnable) { + Realm oldRealm = Realm.getDefault(); + try { + defaultRealm.set(realm); + runnable.run(); + } finally { + defaultRealm.set(oldRealm); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/StaleEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/StaleEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.StaleEvent; + +import java.lang.all; + +/** + * Generic event denoting that the state of an {@link IObservable} object is + * about to change. Note that this event is only fired when an observable + * becomes stale, not when it becomes unstale; an observable that becomes + * unstale should always fire a change event. Staleness can be used (for + * example) to notify listeners when an observable has started a background + * thread for updating its state. Clients can safely ignore staleness. + * + * @see IObservable#isStale() + * + * @since 1.0 + * + */ +public class StaleEvent : ObservableEvent { + + /** + * Creates a new stale event. + * + * @param source + * the source observable + */ + public this(IObservable source) { + super(source); + } + + /** + * + */ + private static final long serialVersionUID = 3491012225431471077L; + + static final Object TYPE = new Object(); + + protected void dispatch(IObservablesListener listener) { + (cast(IStaleListener) listener).handleStale(this); + } + + protected Object getListenerType() { + return TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/AbstractObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/AbstractObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,314 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Brad Reynolds - bug 167204 + * Matthew Hall - bug 118516 + * Matthew Hall - bug 208858 + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.AbstractObservableList; + +import java.lang.all; + +import java.util.AbstractList; +import java.util.Collection; +import java.util.Iterator; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.ChangeSupport; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.AssertionFailedException; + +/** + * Subclasses should override at least get(int index) and size(). + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + * + */ +public abstract class AbstractObservableList : AbstractList , + IObservableList { + + private ChangeSupport changeSupport; + + /** + * @param realm + * + */ + public this(Realm realm) { + Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$ + changeSupport = new class(realm) ChangeSupport { + protected void firstListenerAdded() { + this.outer.firstListenerAdded(); + } + protected void lastListenerRemoved() { + this.outer.lastListenerRemoved(); + } + }; + } + + /** + * + */ + public this() { + this(Realm.getDefault()); + } + + public bool isStale() { + getterCalled(); + return false; + } + + public synchronized void addListChangeListener(IListChangeListener listener) { + changeSupport.addListener(ListChangeEvent.TYPE, listener); + } + + public synchronized void removeListChangeListener(IListChangeListener listener) { + changeSupport.removeListener(ListChangeEvent.TYPE, listener); + } + + protected void fireListChange(ListDiff diff) { + // fire general change event first + fireChange(); + changeSupport.fireEvent(new ListChangeEvent(this, diff)); + } + + public synchronized void addChangeListener(IChangeListener listener) { + changeSupport.addChangeListener(listener); + } + + public synchronized void removeChangeListener(IChangeListener listener) { + changeSupport.removeChangeListener(listener); + } + + public synchronized void addStaleListener(IStaleListener listener) { + changeSupport.addStaleListener(listener); + } + + public synchronized void removeStaleListener(IStaleListener listener) { + changeSupport.removeStaleListener(listener); + } + + /** + * Fires change event. Must be invoked from the current realm. + */ + protected void fireChange() { + checkRealm(); + changeSupport.fireEvent(new ChangeEvent(this)); + } + + /** + * Fires stale event. Must be invoked from the current realm. + */ + protected void fireStale() { + checkRealm(); + changeSupport.fireEvent(new StaleEvent(this)); + } + + /** + * + */ + protected void firstListenerAdded() { + } + + /** + * + */ + protected void lastListenerRemoved() { + } + + /** + * + */ + public synchronized void dispose() { + changeSupport = null; + lastListenerRemoved(); + } + + public final int size() { + getterCalled(); + return doGetSize(); + } + + /** + * @return the size + */ + protected abstract int doGetSize(); + + /** + * + */ + private void getterCalled() { + ObservableTracker.getterCalled(this); + } + + public bool isEmpty() { + getterCalled(); + return super.isEmpty(); + } + + public bool contains(Object o) { + getterCalled(); + return super.contains(o); + } + + public Iterator iterator() { + getterCalled(); + final Iterator wrappedIterator = super.iterator(); + return new class() Iterator { + public void remove() { + wrappedIterator.remove(); + } + + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public Object next() { + return wrappedIterator.next(); + } + }; + } + + public Object[] toArray() { + getterCalled(); + return super.toArray(); + } + + public Object[] toArray(Object a[]) { + getterCalled(); + return super.toArray(a); + } + + // Modification Operations + + public bool add(Object o) { + getterCalled(); + return super.add(o); + } + + /** + * Moves the element located at oldIndex to + * newIndex. This method is equivalent to calling + * add(newIndex, remove(oldIndex)). + *

+ * Subclasses should override this method to deliver list change + * notification for the remove and add operations in the same + * ListChangeEvent, as this allows {@link ListDiff#acceptcast(ListDiffVisitor)} + * to recognize the operation as a move. + * + * @param oldIndex + * the element's position before the move. Must be within the + * range 0 <= oldIndex < size(). + * @param newIndex + * the element's position after the move. Must be within the + * range 0 <= newIndex < size(). + * @return the element that was moved. + * @throws IndexOutOfBoundsException + * if either argument is out of range (0 <= index < size()). + * @see ListDiffVisitor#handleMove(int, int, Object) + * @see ListDiff#acceptcast(ListDiffVisitor) + * @since 1.1 + */ + public Object move(int oldIndex, int newIndex) { + checkRealm(); + int size = doGetSize(); + if (oldIndex < 0 || oldIndex >= size) + throw new IndexOutOfBoundsException( + "oldIndex: " + oldIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + if (newIndex < 0 || newIndex >= size) + throw new IndexOutOfBoundsException( + "newIndex: " + newIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + Object element = remove(oldIndex); + add(newIndex, element); + return element; + } + + public bool remove(Object o) { + getterCalled(); + return super.remove(o); + } + + // Bulk Modification Operations + + public bool containsAll(Collection c) { + getterCalled(); + return super.containsAll(c); + } + + public bool addAll(Collection c) { + getterCalled(); + return super.addAll(c); + } + + public bool addAll(int index, Collection c) { + getterCalled(); + return super.addAll(c); + } + + public bool removeAll(Collection c) { + getterCalled(); + return super.removeAll(c); + } + + public bool retainAll(Collection c) { + getterCalled(); + return super.retainAll(c); + } + + // Comparison and hashing + + public override bool opEquals(Object o) { + getterCalled(); + return super.equals(o); + } + + public int hashCode() { + getterCalled(); + return super.hashCode(); + } + + public int indexOf(Object o) { + getterCalled(); + return super.indexOf(o); + } + + public int lastIndexOf(Object o) { + getterCalled(); + return super.lastIndexOf(o); + } + + public Realm getRealm() { + return changeSupport.getRealm(); + } + + /** + * Asserts that the realm is the current realm. + * + * @see Realm#isCurrent() + * @throws AssertionFailedException + * if the realm is not the current realm + */ + protected void checkRealm() { + Assert.isTrue(getRealm().isCurrent(), + "This operation must be run within the observable's realm"); //$NON-NLS-1$ + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ComputedList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ComputedList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,292 @@ +/************************************************************************************************************ + * Copyright (c) 2007 Matthew Hall and others. 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: + * Matthew Hall - initial API and implementation + * IBM Corporation - initial API and implementation + * Brad Reynolds - initial API and implementation (through bug 116920 and bug 147515) + * Matthew Hall - bug 211786 + ***********************************************************************************************************/ +module org.eclipse.core.databinding.observable.list.ComputedList; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.StaleEvent; + +/** + * A Lazily calculated list that automatically computes and registers listeners + * on its dependencies as long as all of its dependencies are IObservable + * objects + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.1 + */ +public abstract class ComputedList : AbstractObservableList { + private List cachedList = new ArrayList(); + + private bool dirty = true; + private bool stale = false; + + private IObservable[] dependencies = new IObservable[0]; + + /** + * Creates a computed list in the default realm and with an unknown (null) + * element type. + */ + public this() { + this(Realm.getDefault(), null); + } + + /** + * Creates a computed list in the default realm and with the given element + * type. + * + * @param elementType + * the element type, may be null to indicate + * unknown element type + */ + public this(Object elementType) { + this(Realm.getDefault(), elementType); + } + + /** + * Creates a computed list in given realm and with an unknown (null) element + * type. + * + * @param realm + * the realm + * + */ + public this(Realm realm) { + this(realm, null); + } + + /** + * Creates a computed list in the given realm and with the given element + * type. + * + * @param realm + * the realm + * @param elementType + * the element type, may be null to indicate + * unknown element type + */ + public this(Realm realm, Object elementType) { + super(realm); + this.elementType = elementType; + } + + /** + * Inner class that implements interfaces that we don't want to expose as + * public API. Each interface could have been implemented using a separate + * anonymous class, but we combine them here to reduce the memory overhead + * and number of classes. + * + *

+ * The Runnable calls calculate and stores the result in cachedList. + *

+ * + *

+ * The IChangeListener stores each observable in the dependencies list. This + * is registered as the listener when calling ObservableTracker, to detect + * every observable that is used by computeValue. + *

+ * + *

+ * The IChangeListener is attached to every dependency. + *

+ * + */ + private class PrivateInterface : Runnable, IChangeListener, + IStaleListener { + public void run() { + cachedList = calculate(); + if (cachedList is null) + cachedList = Collections.EMPTY_LIST; + } + + public void handleStale(StaleEvent event) { + if (!dirty) + makeStale(); + } + + public void handleChange(ChangeEvent event) { + makeDirty(); + } + } + + private PrivateInterface privateInterface = new PrivateInterface(); + + private Object elementType; + + protected int doGetSize() { + return doGetList().size(); + } + + public Object get(int index) { + getterCalled(); + return doGetList().get(index); + } + + private final List getList() { + getterCalled(); + return doGetList(); + } + + final List doGetList() { + if (dirty) { + // This line will do the following: + // - Run the calculate method + // - While doing so, add any observable that is touched to the + // dependencies list + IObservable[] newDependencies = ObservableTracker.runAndMonitor( + privateInterface, privateInterface, null); + + // If any dependencies are stale, a stale event will be fired here + // even if we were already stale before recomputing. This is in case + // clients assume that a list change is indicative of non-staleness. + stale = false; + for (int i = 0; i < newDependencies.length; i++) { + if (newDependencies[i].isStale()) { + makeStale(); + break; + } + } + + if (!stale) { + for (int i = 0; i < newDependencies.length; i++) { + newDependencies[i].addStaleListener(privateInterface); + } + } + + dependencies = newDependencies; + + dirty = false; + } + + return cachedList; + } + + private void getterCalled() { + ObservableTracker.getterCalled(this); + } + + /** + * Subclasses must override this method to calculate the list contents. + * + * @return the object's list. + */ + protected abstract List calculate(); + + private void makeDirty() { + if (!dirty) { + dirty = true; + + makeStale(); + + stopListening(); + + // copy the old list + final List oldList = new ArrayList(cachedList); + // Fire the "dirty" event. This implementation recomputes the new + // list lazily. + fireListChange(new class() ListDiff { + ListDiffEntry[] differences; + + public ListDiffEntry[] getDifferences() { + if (differences is null) + differences = Diffs.computeListDiff(oldList, getList()) + .getDifferences(); + return differences; + } + }); + } + } + + private void stopListening() { + if (dependencies !is null) { + for (int i = 0; i < dependencies.length; i++) { + IObservable observable = dependencies[i]; + + observable.removeChangeListener(privateInterface); + observable.removeStaleListener(privateInterface); + } + dependencies = null; + } + } + + private void makeStale() { + if (!stale) { + stale = true; + fireStale(); + } + } + + public bool isStale() { + // recalculate list if dirty, to ensure staleness is correct. + getList(); + return stale; + } + + public Object getElementType() { + return elementType; + } + + public synchronized void addChangeListener(IChangeListener listener) { + super.addChangeListener(listener); + // If somebody is listening, we need to make sure we attach our own + // listeners + computeListForListeners(); + } + + public synchronized void addListChangeListener(IListChangeListener listener) { + super.addListChangeListener(listener); + // If somebody is listening, we need to make sure we attach our own + // listeners + computeListForListeners(); + } + + private void computeListForListeners() { + // Some clients just add a listener and expect to get notified even if + // they never called getValue(), so we have to call getValue() ourselves + // here to be sure. Need to be careful about realms though, this method + // can be called outside of our realm. + // See also bug 198211. If a client calls this outside of our realm, + // they may receive change notifications before the runnable below has + // been executed. It is their job to figure out what to do with those + // notifications. + getRealm().exec(new class() Runnable { + public void run() { + if (dependencies is null) { + // We are not currently listening. + // But someone is listening for changes. Call getValue() + // to make sure we start listening to the observables we + // depend on. + getList(); + } + } + }); + } + + public synchronized void dispose() { + stopListening(); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/IListChangeListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/IListChangeListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.IListChangeListener; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; + +/** + * Listener for changes to observable lists. + * + * @since 1.0 + */ +public interface IListChangeListener : IObservablesListener { + + /** + * Handle a change to an observable list. The change is described by the + * diff object. The given event object must only be used locally in this + * method because it may be reused for other change notifications. The diff + * object referenced by the event is immutable and may be used non-locally. + * + * @param event + */ + void handleListChange(ListChangeEvent event); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/IObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/IObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 167204 + * Matthew Hall - bug 208858 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.IObservableList; + +import java.lang.all; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.core.databinding.observable.IObservableCollection; + +/** + * A list whose changes can be tracked by list change listeners. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should instead subclass one of the framework classes + * that implement this interface. Note that direct implementers of + * this interface outside of the framework will be broken in future + * releases when methods are added to this interface. + * + * @see AbstractObservableList + * @see ObservableList + * + * @since 1.0 + */ +public interface IObservableList : List, IObservableCollection { + + /** + * Adds the given list change listener to the list of list change listeners. + * @param listener + */ + public void addListChangeListener(IListChangeListener listener); + + /** + * Removes the given list change listener from the list of list change listeners. + * Has no effect if the given listener is not registered as a list change listener. + * + * @param listener + */ + public void removeListChangeListener(IListChangeListener listener); + + /** + * @TrackedGetter + */ + public int size(); + + /** + * @TrackedGetter + */ + public bool isEmpty(); + + /** + * @TrackedGetter + */ + public bool contains(Object o); + + /** + * @TrackedGetter + */ + public Iterator iterator(); + + /** + * @TrackedGetter + */ + public Object[] toArray(); + + /** + * @TrackedGetter + */ + public Object[] toArray(Object a[]); + + /** + * + */ + public bool add(Object o); + + /** + * + */ + public bool remove(Object o); + + /** + * @TrackedGetter + */ + public bool containsAll(Collection c); + + /** + * + */ + public bool addAll(Collection c); + + /** + * + */ + public bool addAll(int index, Collection c); + + /** + * + */ + public bool removeAll(Collection c); + + /** + * + */ + public bool retainAll(Collection c); + + /** + * @TrackedGetter + */ + public bool opEquals(Object o); + + /** + * @TrackedGetter + */ + public int hashCode(); + + /** + * @TrackedGetter + */ + public Object get(int index); + + /** + * + */ + public Object set(int index, Object element); + + /** + * Moves the element located at oldIndex to + * newIndex. This method is equivalent to calling + * add(newIndex, remove(oldIndex)). + *

+ * Implementors should deliver list change notification for the remove and + * add operations in the same ListChangeEvent, as this allows + * {@link ListDiff#acceptcast(ListDiffVisitor)} to recognize the operation as a + * move. + * + * @param oldIndex + * the element's position before the move. Must be within the + * range 0 <= oldIndex < size(). + * @param newIndex + * the element's position after the move. Must be within the + * range 0 <= newIndex < size(). + * @return the element that was moved. + * @throws IndexOutOfBoundsException + * if either argument is out of range (0 <= index < size()). + * @see ListDiffVisitor#handleMove(int, int, Object) + * @see ListDiff#acceptcast(ListDiffVisitor) + * @since 1.1 + */ + public Object move(int oldIndex, int newIndex); + + /** + * + */ + public Object remove(int index); + + /** + * @TrackedGetter + */ + public int indexOf(Object o); + + /** + * @TrackedGetter + */ + public int lastIndexOf(Object o); + + /** + * @TrackedGetter + */ + public ListIterator listIterator(); + + /** + * @TrackedGetter + */ + public ListIterator listIterator(int index); + + /** + * @TrackedGetter + */ + public List subList(int fromIndex, int toIndex); + + /** + * @return the type of the elements or null if untyped + */ + Object getElementType(); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListChangeEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListChangeEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.ListChangeEvent; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.ObservableEvent; + +/** + * List change event describing an incremental change of an + * {@link IObservableList} object. + * + * @since 1.0 + */ +public class ListChangeEvent : ObservableEvent { + + /** + * + */ + private static final long serialVersionUID = -9154315534258776672L; + + static final Object TYPE = new Object(); + + /** + * Description of the change to the source observable list. Listeners must + * not change this field. + */ + public ListDiff diff; + + /** + * Creates a new list change event. + * + * @param source + * the source observable list + * @param diff + * the list change + */ + public this(IObservableList source, ListDiff diff) { + super(source); + this.diff = diff; + } + + /** + * Returns the observable list from which this event originated. + * + * @return the observable list from which this event originated + */ + public IObservableList getObservableList() { + return cast(IObservableList) getSource(); + } + + protected void dispatch(IObservablesListener listener) { + (cast(IListChangeListener) listener).handleListChange(this); + } + + protected Object getListenerType() { + return TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiff.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiff.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208858 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.ListDiff; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.Util; + +/** + * Object describing a diff between two lists. + * + * @since 1.0 + */ +public abstract class ListDiff { + + /** + * Returns a ListDiffEntry array representing the differences in the list, + * in the order they are to be processed. + * + * @return a ListDiffEntry array representing the differences in the list, + * in the order they are to be processed. + */ + public abstract ListDiffEntry[] getDifferences(); + + /** + * Traverses the {@link #getDifferences()} array, calling the appropriate + * method in visitor for each difference. + *

    + *
  1. {@link ListDiffVisitor#handleReplace(int, Object, Object)} is called + * whenever a remove entry is immediately followed by an add entry which + * shares the same list index. + *
  2. {@link ListDiffVisitor#handleMove(int, int, Object)} is called + * whenever a remove entry is immediately followed by an add entry with an + * equivalent element. + *
  3. {@link ListDiffVisitor#handleRemove(int, Object)} is called whenever + * a remove entry does not match conditions 1 or 2. + *
  4. {@link ListDiffVisitor#handleAdd(int, Object)} is called whenever an + * add entry does not match conditions in 1 or 2. + *
+ * + * @param visitor + * the visitor to receive callbacks. + * @see ListDiffVisitor + * @since 1.1 + */ + public void accept(ListDiffVisitor visitor) { + ListDiffEntry[] differences = getDifferences(); + for (int i = 0; i < differences.length; i++) { + ListDiffEntry entry = differences[i]; + int position = entry.getPosition(); + Object element = entry.getElement(); + bool addition = entry.isAddition(); + + if (!addition && i + 1 < differences.length) { + ListDiffEntry entry2 = differences[i + 1]; + if (entry2.isAddition()) { + int position2 = entry2.getPosition(); + Object element2 = entry2.getElement(); + if (position is position2) { + visitor.handleReplace(position, element, element2); + i++; + continue; + } + if (Util.equals(element, element2)) { + visitor.handleMove(position, position2, element); + i++; + continue; + } + } + } + if (addition) + visitor.handleAdd(position, element); + else + visitor.handleRemove(position, element); + } + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + ListDiffEntry[] differences = getDifferences(); + StringBuffer buffer = new StringBuffer(); + buffer.append(getClass().getName()); + + if (differences is null || differences.length is 0) { + buffer + .append("{}"); //$NON-NLS-1$ + } else { + buffer + .append("{"); //$NON-NLS-1$ + + for (int i = 0; i < differences.length; i++) { + if (i > 0) + buffer.append(", "); //$NON-NLS-1$ + + buffer + .append("difference[") //$NON-NLS-1$ + .append(i) + .append("] [") //$NON-NLS-1$ + .append(differences[i] !is null ? differences[i].toString() : "null") //$NON-NLS-1$ + .append("]"); //$NON-NLS-1$ + } + buffer.append("}"); //$NON-NLS-1$ + } + + return buffer.toString(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiffEntry.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiffEntry.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.ListDiffEntry; + +import java.lang.all; + +/** + * A single addition of an element to a list or removal of an element from a list. + * + * @since 1.0 + */ +public abstract class ListDiffEntry { + + /** + * @return the 0-based position of the addition or removal + */ + public abstract int getPosition(); + + /** + * @return true if this represents an addition, false if this represents a removal + */ + public abstract bool isAddition(); + + /** + * @return the element that was added or removed + */ + public abstract Object getElement(); + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer + .append(this.getClass().getName()) + .append("{position [") //$NON-NLS-1$ + .append(getPosition()) + .append("], isAddition [") //$NON-NLS-1$ + .append(isAddition()) + .append("], element [") //$NON-NLS-1$ + .append(getElement() !is null ? getElement().toString() : "null") //$NON-NLS-1$ + .append("]}"); //$NON-NLS-1$ + + return buffer.toString(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiffVisitor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiffVisitor.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2007 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 208858) + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.ListDiffVisitor; + +import java.lang.all; + +import java.util.List; + +/** + * A visitor for processing differences in a ListDiff. + * + * @see ListDiff#acceptcast(ListDiffVisitor) + * @since 1.1 + */ +public abstract class ListDiffVisitor { + /** + * Notifies the visitor that element was added to the list at + * position index. + * + * @param index + * the index where the element was added + * @param element + * the element that was added + */ + public abstract void handleAdd(int index, Object element); + + /** + * Notifies the visitor that element was removed from the + * list at position index. + * + * @param index + * the index where the element was removed + * @param element + * the element that was removed + */ + public abstract void handleRemove(int index, Object element); + + /** + * Notifies the visitor that element was moved in the list + * from position oldIndex to position newIndex. + *

+ * The default implementation of this method calls + * {@link #handleRemove(int, Object)} with the old position, then + * {@link #handleAdd(int, Object)} with the new position. Clients which are + * interested in recognizing "moves" in a list (i.e. calls to + * {@link IObservableList#move(int, int)}) should override this method. + * + * @param oldIndex + * the index that the element was moved from. + * @param newIndex + * the index that the element was moved to. + * @param element + * the element that was moved + * @see IObservableList#move(int, int) + */ + public void handleMove(int oldIndex, int newIndex, Object element) { + handleRemove(oldIndex, element); + handleAdd(newIndex, element); + } + + /** + * Notifies the visitor that oldElement, located at position + * index in the list, was replaced by newElement. + *

+ * The default implementation of this method calls + * {@link #handleRemove(int, Object)} with the old element, then + * {@link #handleAdd(int, Object)} with the new element. Clients which are + * interested in recognizing "replaces" in a list (i.e. calls to + * {@link List#set(int, Object)}) should override this method. + * + * @param index + * the index where the element was replaced. + * @param oldElement + * the element being replaced. + * @param newElement + * the element that replaced oldElement. + * @see List#set(int, Object) + */ + public void handleReplace(int index, Object oldElement, Object newElement) { + handleRemove(index, oldElement); + handleAdd(index, newElement); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,379 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Brad Reynolds - bug 167204 + * Matthew Hall - bug 208858 + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.list.ObservableList; + +import java.lang.all; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.core.databinding.observable.AbstractObservable; +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; + +/** + * + * Abstract implementation of {@link IObservableList}, based on an underlying regular list. + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + * + */ +public abstract class ObservableList : AbstractObservable , + IObservableList { + + protected List wrappedList; + + /** + * Stale state of the list. Access must occur in the current realm. + */ + private bool stale = false; + + private Object elementType; + + protected this(List wrappedList, Object elementType) { + this(Realm.getDefault(), wrappedList, elementType); + } + + protected this(Realm realm, List wrappedList, Object elementType) { + super(realm); + this.wrappedList = wrappedList; + this.elementType = elementType; + } + + public synchronized void addListChangeListener(IListChangeListener listener) { + addListener(ListChangeEvent.TYPE, listener); + } + + public synchronized void removeListChangeListener(IListChangeListener listener) { + removeListener(ListChangeEvent.TYPE, listener); + } + + protected void fireListChange(ListDiff diff) { + // fire general change event first + super.fireChange(); + fireEvent(new ListChangeEvent(this, diff)); + } + + public bool contains(Object o) { + getterCalled(); + return wrappedList.contains(o); + } + + public bool containsAll(Collection c) { + getterCalled(); + return wrappedList.containsAll(c); + } + + public override bool opEquals(Object o) { + getterCalled(); + return wrappedList.equals(o); + } + + public int hashCode() { + getterCalled(); + return wrappedList.hashCode(); + } + + public bool isEmpty() { + getterCalled(); + return wrappedList.isEmpty(); + } + + public Iterator iterator() { + getterCalled(); + final Iterator wrappedIterator = wrappedList.iterator(); + return new class() Iterator { + + public void remove() { + throw new UnsupportedOperationException(); + } + + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public Object next() { + return wrappedIterator.next(); + } + }; + } + + public int size() { + getterCalled(); + return wrappedList.size(); + } + + public Object[] toArray() { + getterCalled(); + return wrappedList.toArray(); + } + + public Object[] toArray(Object[] a) { + getterCalled(); + return wrappedList.toArray(a); + } + + public String toString() { + getterCalled(); + return wrappedList.toString(); + } + + /** + * @TrackedGetter + */ + public Object get(int index) { + getterCalled(); + return wrappedList.get(index); + } + + /** + * @TrackedGetter + */ + public int indexOf(Object o) { + getterCalled(); + return wrappedList.indexOf(o); + } + + /** + * @TrackedGetter + */ + public int lastIndexOf(Object o) { + getterCalled(); + return wrappedList.lastIndexOf(o); + } + + // List Iterators + + /** + * @TrackedGetter + */ + public ListIterator listIterator() { + return listIterator(0); + } + + /** + * @TrackedGetter + */ + public ListIterator listIterator(int index) { + getterCalled(); + final ListIterator wrappedIterator = wrappedList.listIterator(index); + return new class() ListIterator { + + public int nextIndex() { + return wrappedIterator.nextIndex(); + } + + public int previousIndex() { + return wrappedIterator.previousIndex(); + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public bool hasPrevious() { + return wrappedIterator.hasPrevious(); + } + + public Object next() { + return wrappedIterator.next(); + } + + public Object previous() { + return wrappedIterator.previous(); + } + + public void add(Object o) { + throw new UnsupportedOperationException(); + } + + public void set(Object o) { + throw new UnsupportedOperationException(); + } + }; + } + + + public List subList(int fromIndex, int toIndex) { + getterCalled(); + if (fromIndex < 0 || fromIndex > toIndex || toIndex > size()) { + throw new IndexOutOfBoundsException(); + } + return new class(getRealm(), fromIndex, toIndex ) AbstractObservableList { + int fromIndex_; int toIndex_; + this( Realm r, int f, int t){ + super(r); + fromIndex_ = r; + toIndex_ = t; + } + + public Object getElementType() { + return this.outer.getElementType(); + } + + public Object get(int location) { + return this.outer.get(fromIndex_ + location); + } + + protected int doGetSize() { + return toIndex_ - fromIndex_; + } + }; + } + + protected void getterCalled() { + ObservableTracker.getterCalled(this); + } + + public Object set(int index, Object element) { + throw new UnsupportedOperationException(); + } + + /** + * Moves the element located at oldIndex to + * newIndex. This method is equivalent to calling + * add(newIndex, remove(oldIndex)). + *

+ * Subclasses should override this method to deliver list change + * notification for the remove and add operations in the same + * ListChangeEvent, as this allows {@link ListDiff#acceptcast(ListDiffVisitor)} + * to recognize the operation as a move. + * + * @param oldIndex + * the element's position before the move. Must be within the + * range 0 <= oldIndex < size(). + * @param newIndex + * the element's position after the move. Must be within the + * range 0 <= newIndex < size(). + * @return the element that was moved. + * @throws IndexOutOfBoundsException + * if either argument is out of range (0 <= index < size()). + * @see ListDiffVisitor#handleMove(int, int, Object) + * @see ListDiff#acceptcast(ListDiffVisitor) + * @since 1.1 + */ + public Object move(int oldIndex, int newIndex) { + checkRealm(); + int size = wrappedList.size(); + if (oldIndex < 0 || oldIndex >= size) + throw new IndexOutOfBoundsException( + "oldIndex: " + oldIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + if (newIndex < 0 || newIndex >= size) + throw new IndexOutOfBoundsException( + "newIndex: " + newIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + Object element = remove(oldIndex); + add(newIndex, element); + return element; + } + + public Object remove(int index) { + throw new UnsupportedOperationException(); + } + + public bool add(Object o) { + throw new UnsupportedOperationException(); + } + + public void add(int index, Object element) { + throw new UnsupportedOperationException(); + } + + public bool addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + + public bool remove(Object o) { + throw new UnsupportedOperationException(); + } + + public bool removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * Returns the stale state. Must be invoked from the current realm. + * + * @return stale state + */ + public bool isStale() { + getterCalled(); + return stale; + } + + /** + * Sets the stale state. Must be invoked from the current realm. + * + * @param stale + * The stale state to list. This will fire a stale event if the + * given bool is true and this observable list was not already + * stale. + */ + public void setStale(bool stale) { + checkRealm(); + + bool wasStale = this.stale; + this.stale = stale; + if (!wasStale && stale) { + fireStale(); + } + } + + protected void fireChange() { + throw new RuntimeException("fireChange should not be called, use fireListChange() instead"); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.jface.provisional.databinding.observable.AbstractObservable#dispose() + */ + public synchronized void dispose() { + super.dispose(); + } + + public Object getElementType() { + return elementType; + } + + protected void updateWrappedList(List newList) { + List oldList = wrappedList; + ListDiff listDiff = Diffs.computeListDiff(oldList, newList); + wrappedList = newList; + fireListChange(listDiff); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/WritableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/WritableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Brad Reynolds - bug 167204 + * Gautam Saggar - bug 169529 + * Brad Reynolds - bug 147515 + * Matthew Hall - bug 208858, 213145 + *******************************************************************************/ +module org.eclipse.core.databinding.observable.list.WritableList; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; + +/** + * Mutable observable list backed by an ArrayList. + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + */ +public class WritableList : ObservableList { + + /** + * Creates an empty writable list in the default realm with a + * null element type. + * + */ + public this() { + this(Realm.getDefault()); + } + + /** + * Creates an empty writable list with a null element type. + * + * @param realm + */ + public this(Realm realm) { + this(realm, new ArrayList(), null); + } + + /** + * Constructs a new instance with the default realm. + * + * @param toWrap + * @param elementType + * can be null + */ + public this(List toWrap, Object elementType) { + this(Realm.getDefault(), toWrap, elementType); + } + + /** + * Creates a writable list containing elements of the given type, wrapping + * an existing client-supplied list. + * + * @param realm + * @param toWrap + * The java.utilList to wrap + * @param elementType + * can be null + */ + public this(Realm realm, List toWrap, Object elementType) { + super(realm, toWrap, elementType); + } + + public Object set(int index, Object element) { + checkRealm(); + Object oldElement = wrappedList.set(index, element); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index, + false, oldElement), Diffs.createListDiffEntry(index, true, + element))); + return oldElement; + } + + /** + * @since 1.1 + */ + public Object move(int oldIndex, int newIndex) { + checkRealm(); + int size = wrappedList.size(); + if (oldIndex < 0 || oldIndex >= size) + throw new IndexOutOfBoundsException( + "oldIndex: " + oldIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + if (newIndex < 0 || newIndex >= size) + throw new IndexOutOfBoundsException( + "newIndex: " + newIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + if (oldIndex is newIndex) + return wrappedList.get(oldIndex); + Object element = wrappedList.remove(oldIndex); + wrappedList.add(newIndex, element); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(oldIndex, + false, element), Diffs.createListDiffEntry(newIndex, true, + element))); + return element; + } + + public Object remove(int index) { + checkRealm(); + Object oldElement = wrappedList.remove(index); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index, + false, oldElement))); + return oldElement; + } + + public bool add(Object element) { + checkRealm(); + bool added = wrappedList.add(element); + if (added) { + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry( + wrappedList.size() - 1, true, element))); + } + return added; + } + + public void add(int index, Object element) { + checkRealm(); + wrappedList.add(index, element); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index, + true, element))); + } + + public bool addAll(Collection c) { + checkRealm(); + ListDiffEntry[] entries = new ListDiffEntry[c.size()]; + int i = 0; + int addIndex = wrappedList.size(); + for (Iterator it = c.iterator(); it.hasNext();) { + Object element = it.next(); + entries[i++] = Diffs.createListDiffEntry(addIndex++, true, element); + } + bool added = wrappedList.addAll(c); + fireListChange(Diffs.createListDiff(entries)); + return added; + } + + public bool addAll(int index, Collection c) { + checkRealm(); + ListDiffEntry[] entries = new ListDiffEntry[c.size()]; + int i = 0; + int addIndex = index; + for (Iterator it = c.iterator(); it.hasNext();) { + Object element = it.next(); + entries[i++] = Diffs.createListDiffEntry(addIndex++, true, element); + } + bool added = wrappedList.addAll(index, c); + fireListChange(Diffs.createListDiff(entries)); + return added; + } + + public bool remove(Object o) { + checkRealm(); + int index = wrappedList.indexOf(o); + if (index is -1) { + return false; + } + wrappedList.remove(index); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index, + false, o))); + return true; + } + + public bool removeAll(Collection c) { + checkRealm(); + List entries = new ArrayList(); + for (Iterator it = c.iterator(); it.hasNext();) { + Object element = it.next(); + int removeIndex = wrappedList.indexOf(element); + if (removeIndex !is -1) { + wrappedList.remove(removeIndex); + entries.add(Diffs.createListDiffEntry(removeIndex, false, + element)); + } + } + if (entries.size() > 0) + fireListChange(Diffs.createListDiff(cast(ListDiffEntry[]) entries + .toArray(new ListDiffEntry[entries.size()]))); + return entries.size() > 0; + } + + public bool retainAll(Collection c) { + checkRealm(); + List entries = new ArrayList(); + int removeIndex = 0; + for (Iterator it = wrappedList.iterator(); it.hasNext();) { + Object element = it.next(); + if (!c.contains(element)) { + entries.add(Diffs.createListDiffEntry(removeIndex, false, + element)); + it.remove(); + } else { + // only increment if we haven't removed the current element + removeIndex++; + } + } + if (entries.size() > 0) + fireListChange(Diffs.createListDiff(cast(ListDiffEntry[]) entries + .toArray(new ListDiffEntry[entries.size()]))); + return entries.size() > 0; + } + + public void clear() { + checkRealm(); + List entries = new ArrayList(); + for (Iterator it = wrappedList.iterator(); it.hasNext();) { + Object element = it.next(); + // always report 0 as the remove index + entries.add(Diffs.createListDiffEntry(0, false, element)); + it.remove(); + } + fireListChange(Diffs.createListDiff(cast(ListDiffEntry[]) entries + .toArray(new ListDiffEntry[entries.size()]))); + } + + /** + * @param elementType + * can be null + * @return new list with the default realm. + */ + public static WritableList withElementType(Object elementType) { + return new WritableList(Realm.getDefault(), new ArrayList(), + elementType); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides classes for observing changes in lists. +

+Package Specification

+

+This package provides classes for observing changes in lists.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/AbstractObservableMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/AbstractObservableMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,166 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Matthew Hall - bug 118516 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.AbstractObservableMap; + +import java.lang.all; + +import java.util.AbstractMap; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.ChangeSupport; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.AssertionFailedException; + +/** + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + */ +public abstract class AbstractObservableMap : AbstractMap , + IObservableMap { + + private ChangeSupport changeSupport; + + private bool stale; + + /** + */ + public this() { + this(Realm.getDefault()); + } + + /** + * + */ + protected void lastListenerRemoved() { + } + + /** + * + */ + protected void firstListenerAdded() { + } + + /** + * @param realm + */ + public this(Realm realm) { + Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$ + changeSupport = new class(realm) ChangeSupport { + protected void firstListenerAdded() { + this.outer.firstListenerAdded(); + } + protected void lastListenerRemoved() { + this.outer.lastListenerRemoved(); + } + }; + } + + public synchronized void addMapChangeListener(IMapChangeListener listener) { + changeSupport.addListener(MapChangeEvent.TYPE, listener); + } + + public synchronized void removeMapChangeListener(IMapChangeListener listener) { + changeSupport.removeListener(MapChangeEvent.TYPE, listener); + } + + public synchronized void addChangeListener(IChangeListener listener) { + changeSupport.addChangeListener(listener); + } + + public synchronized void addStaleListener(IStaleListener listener) { + changeSupport.addStaleListener(listener); + } + + public synchronized void dispose() { + changeSupport.dispose(); + changeSupport = null; + } + + public Realm getRealm() { + return changeSupport.getRealm(); + } + + public bool isStale() { + checkRealm(); + return stale; + } + + public synchronized void removeChangeListener(IChangeListener listener) { + changeSupport.removeChangeListener(listener); + } + + public synchronized void removeStaleListener(IStaleListener listener) { + changeSupport.removeStaleListener(listener); + } + + /** + * Sets the stale state. Must be invoked from the current realm. + * + * @param stale + */ + public void setStale(bool stale) { + checkRealm(); + this.stale = stale; + if (stale) { + fireStale(); + } + } + + /** + * Fires stale events. Must be invoked from current realm. + */ + protected void fireStale() { + checkRealm(); + changeSupport.fireEvent(new StaleEvent(this)); + } + + /** + * Fires change events. Must be invoked from current realm. + */ + protected void fireChange() { + checkRealm(); + changeSupport.fireEvent(new ChangeEvent(this)); + } + + /** + * Fires map change events. Must be invoked from current realm. + * + * @param diff + */ + protected void fireMapChange(MapDiff diff) { + checkRealm(); + changeSupport.fireEvent(new MapChangeEvent(this, diff)); + } + + /** + * Asserts that the realm is the current realm. + * + * @see Realm#isCurrent() + * @throws AssertionFailedException + * if the realm is not the current realm + */ + protected void checkRealm() { + Assert.isTrue(getRealm().isCurrent(), + "This operation must be run within the observable's realm"); //$NON-NLS-1$ + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/BidirectionalMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/BidirectionalMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.observable.map.BidirectionalMap; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Realm; + +/** + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + * + */ +public class BidirectionalMap : ObservableMap { + + private Map valueToElements = new HashMap(); + + private IMapChangeListener mapListener = new class() IMapChangeListener { + + public void handleMapChange(MapChangeEvent event) { + MapDiff diff = event.diff; + for (Iterator it = diff.getAddedKeys().iterator(); it.hasNext();) { + Object addedKey = it.next(); + addMapping(addedKey, diff.getNewValue(addedKey)); + } + for (Iterator it = diff.getChangedKeys().iterator(); it.hasNext();) { + Object changedKey = it.next(); + removeMapping(changedKey, diff.getOldValue(changedKey)); + addMapping(changedKey, diff.getNewValue(changedKey)); + } + for (Iterator it = diff.getRemovedKeys().iterator(); it.hasNext();) { + Object removedKey = it.next(); + removeMapping(removedKey, diff.getOldValue(removedKey)); + } + fireMapChange(diff); + } + }; + + /** + * @param wrappedMap + */ + public this(IObservableMap wrappedMap) { + super(wrappedMap.getRealm(), wrappedMap); + wrappedMap.addMapChangeListener(mapListener); + for (Iterator it = wrappedMap.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = cast(Entry) it.next(); + addMapping(entry.getKey(), entry.getValue()); + } + } + + /** + * @param key + * @param value + */ + private void addMapping(Object key, Object value) { + Object elementOrSet = valueToElements.get(value); + if (elementOrSet is null) { + valueToElements.put(value, key); + return; + } + if (!( null !is cast(Set)elementOrSet )) { + elementOrSet = new HashSet(Collections.singleton(elementOrSet)); + valueToElements.put(value, elementOrSet); + } + Set set = cast(Set) elementOrSet; + set.add(key); + } + + /** + * @param functionValue + * @param element + */ + private void removeMapping(Object functionValue, Object element) { + Object elementOrSet = valueToElements.get(functionValue); + if ( null !is cast(Set)elementOrSet ) { + Set set = cast(Set) elementOrSet; + set.remove(element); + if (set.size() is 0) { + valueToElements.remove(functionValue); + } + } else { + valueToElements.remove(functionValue); + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/CompositeMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/CompositeMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,363 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.observable.map.CompositeMap; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.set.WritableSet; +import org.eclipse.core.runtime.Assert; + +/** + * A read-only observable map formed by the composition of two observable maps. + * If map1 maps keys a:A to values b1:B, and map2 maps keys b2:B to values c:C, + * the composite map maps keys a:A to values c:C. For example, map1 could map + * Order objects to their corresponding Customer objects, and map2 could map + * Customer objects to their "last name" property of type String. The composite + * map of map1 and map2 would then map Order objects to their customers' last + * names. + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.1 + * + */ +public class CompositeMap : ObservableMap { + + private Map valueToElements = new HashMap(); + + // adds that need to go through the second map and thus will be picked up by + // secondMapListener. + private Set pendingAdds = new HashSet(); + + // Removes that need to go through the second map and thus will be picked up + // by + // secondMapListener. Maps from value being removed to key being removed. + private Map pendingRemoves = new HashMap(); + + // Changes that need to go through the second map and thus will be picked up + // by + // secondMapListener. Maps from old value to new value and new value to old + // value. + private Map pendingChanges = new HashMap(); + + private IMapChangeListener firstMapListener = new class() IMapChangeListener { + + public void handleMapChange(MapChangeEvent event) { + MapDiff diff = event.diff; + Set rangeSetAdditions = new HashSet(); + Set rangeSetRemovals = new HashSet(); + final Set adds = new HashSet(); + final Set changes = new HashSet(); + final Set removes = new HashSet(); + final Map oldValues = new HashMap(); + + for (Iterator it = diff.getAddedKeys().iterator(); it.hasNext();) { + Object addedKey = it.next(); + Object newValue = diff.getNewValue(addedKey); + addMapping(addedKey, newValue); + if (!rangeSet.contains(newValue)) { + pendingAdds.add(newValue); + rangeSetAdditions.add(newValue); + } else { + adds.add(addedKey); + wrappedMap.put(addedKey, secondMap.get(newValue)); + } + } + for (Iterator it = diff.getChangedKeys().iterator(); it.hasNext();) { + Object changedKey = it.next(); + Object oldValue = diff.getOldValue(changedKey); + Object newValue = diff.getNewValue(changedKey); + bool removed = removeMapping(changedKey, oldValue); + addMapping(changedKey, newValue); + bool added = !rangeSet.contains(newValue); + if (removed) { + pendingRemoves.put(oldValue, changedKey); + rangeSetRemovals.add(oldValue); + } + if (added) { + pendingAdds.add(newValue); + rangeSetAdditions.add(newValue); + } + if (added || removed) { + pendingChanges.put(oldValue, newValue); + pendingChanges.put(newValue, oldValue); + } else { + changes.add(changedKey); + oldValues.put(changedKey, oldValue); + wrappedMap.put(changedKey, secondMap.get(newValue)); + } + } + for (Iterator it = diff.getRemovedKeys().iterator(); it.hasNext();) { + Object removedKey = it.next(); + Object oldValue = diff.getOldValue(removedKey); + if (removeMapping(removedKey, oldValue)) { + pendingRemoves.put(oldValue, removedKey); + rangeSetRemovals.add(oldValue); + } else { + removes.add(removedKey); + oldValues.put(removedKey, secondMap.get(oldValue)); + wrappedMap.remove(removedKey); + } + } + + if (adds.size() > 0 || removes.size() > 0 || changes.size() > 0) { + fireMapChange(new class() MapDiff { + + public Set getAddedKeys() { + return adds; + } + + public Set getChangedKeys() { + return changes; + } + + public Object getNewValue(Object key) { + return wrappedMap.get(key); + } + + public Object getOldValue(Object key) { + return oldValues.get(key); + } + + public Set getRemovedKeys() { + return removes; + } + }); + } + + if (rangeSetAdditions.size() > 0 || rangeSetRemovals.size() > 0) { + rangeSet.addAndRemove(rangeSetAdditions, rangeSetRemovals); + } + } + }; + + private IMapChangeListener secondMapListener = new class() IMapChangeListener { + + public void handleMapChange(MapChangeEvent event) { + MapDiff diff = event.diff; + final Set adds = new HashSet(); + final Set changes = new HashSet(); + final Set removes = new HashSet(); + final Map oldValues = new HashMap(); + final Map newValues = new HashMap(); + Set addedKeys = new HashSet(diff.getAddedKeys()); + Set removedKeys = new HashSet(diff.getRemovedKeys()); + + for (Iterator it = addedKeys.iterator(); it.hasNext();) { + Object addedKey = it.next(); + Set elements = getElementsForValue(addedKey); + Object newValue = diff.getNewValue(addedKey); + if (pendingChanges.containsKey(addedKey)) { + Object oldKey = pendingChanges.remove(addedKey); + Object oldValue; + if (removedKeys.remove(oldKey)) { + oldValue = diff.getOldValue(oldKey); + } else { + oldValue = secondMap.get(oldKey); + } + pendingChanges.remove(oldKey); + pendingAdds.remove(addedKey); + pendingRemoves.remove(oldKey); + for (Iterator it2 = elements.iterator(); it2.hasNext();) { + Object element = it2.next(); + changes.add(element); + oldValues.put(element, oldValue); + newValues.put(element, newValue); + wrappedMap.put(element, newValue); + } + } else if (pendingAdds.remove(addedKey)) { + for (Iterator it2 = elements.iterator(); it2.hasNext();) { + Object element = it2.next(); + adds.add(element); + newValues.put(element, newValue); + wrappedMap.put(element, newValue); + } + } else { + Assert.isTrue(false, "unexpected case"); //$NON-NLS-1$ + } + } + for (Iterator it = diff.getChangedKeys().iterator(); it.hasNext();) { + Object changedKey = it.next(); + Set elements = getElementsForValue(changedKey); + for (Iterator it2 = elements.iterator(); it2.hasNext();) { + Object element = it2.next(); + changes.add(element); + oldValues.put(element, diff.getOldValue(changedKey)); + Object newValue = diff.getNewValue(changedKey); + newValues.put(element, newValue); + wrappedMap.put(element, newValue); + } + } + for (Iterator it = removedKeys.iterator(); it.hasNext();) { + Object removedKey = it.next(); + Object element = pendingRemoves.remove(removedKey); + if (element !is null) { + if (pendingChanges.containsKey(removedKey)) { + Object newKey = pendingChanges.remove(removedKey); + pendingChanges.remove(newKey); + pendingAdds.remove(newKey); + pendingRemoves.remove(removedKey); + changes.add(element); + oldValues.put(element, diff.getOldValue(removedKey)); + Object newValue = secondMap.get(newKey); + newValues.put(element, newValue); + wrappedMap.put(element, newValue); + } else { + removes.add(element); + Object oldValue = diff.getOldValue(removedKey); + oldValues.put(element, oldValue); + wrappedMap.remove(element); + } + } else { + Assert.isTrue(false, "unexpected case"); //$NON-NLS-1$ + } + } + + if (adds.size() > 0 || removes.size() > 0 || changes.size() > 0) { + fireMapChange(new class() MapDiff { + + public Set getAddedKeys() { + return adds; + } + + public Set getChangedKeys() { + return changes; + } + + public Object getNewValue(Object key) { + return newValues.get(key); + } + + public Object getOldValue(Object key) { + return oldValues.get(key); + } + + public Set getRemovedKeys() { + return removes; + } + }); + } + } + }; + + private IObservableMap firstMap; + private IObservableMap secondMap; + + private static class WritableSetPlus : WritableSet { + void addAndRemove(Set additions, Set removals) { + wrappedSet.removeAll(removals); + wrappedSet.addAll(additions); + fireSetChange(Diffs.createSetDiff(additions, removals)); + } + } + + private WritableSetPlus rangeSet = new WritableSetPlus(); + + /** + * Creates a new composite map. Because the key set of the second map is + * determined by the value set of the given observable map + * firstMap, it cannot be passed in as an argument. Instead, + * the second map will be created by calling + * secondMapFactory.createObservable(valueSet()). + * + * @param firstMap + * the first map + * @param secondMapFactory + * a factory that creates the second map when given an observable + * set representing the value set of firstMap. + */ + public this(IObservableMap firstMap, + IObservableFactory secondMapFactory) { + super(firstMap.getRealm(), new HashMap()); + this.firstMap = firstMap; + firstMap.addMapChangeListener(firstMapListener); + for (Iterator it = firstMap.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = cast(Entry) it.next(); + addMapping(entry.getKey(), entry.getValue()); + rangeSet.add(entry.getValue()); + } + this.secondMap = cast(IObservableMap) secondMapFactory + .createObservable(rangeSet); + secondMap.addMapChangeListener(secondMapListener); + for (Iterator it = firstMap.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = cast(Entry) it.next(); + wrappedMap.put(entry.getKey(), secondMap.get(entry.getValue())); + } + } + + /** + * @param key + * @param value + */ + private void addMapping(Object key, Object value) { + Object elementOrSet = valueToElements.get(value); + if (elementOrSet is null) { + valueToElements.put(value, key); + return; + } + if (!( null !is cast(Set)elementOrSet )) { + elementOrSet = new HashSet(Collections.singleton(elementOrSet)); + valueToElements.put(value, elementOrSet); + } + Set set = cast(Set) elementOrSet; + set.add(key); + } + + /** + * @param key + * @param value + */ + private bool removeMapping(Object key, Object value) { + Object elementOrSet = valueToElements.get(value); + if ( null !is cast(Set)elementOrSet ) { + Set set = cast(Set) elementOrSet; + set.remove(key); + if (set.size() is 0) { + valueToElements.remove(value); + return true; + } + return false; + } + valueToElements.remove(value); + return true; + } + + private Set getElementsForValue(Object value) { + Object elementOrSet = valueToElements.get(value); + if ( null !is cast(Set)elementOrSet ) { + return cast(Set) elementOrSet; + } + return elementOrSet is null ? Collections.EMPTY_SET : Collections + .singleton(elementOrSet); + } + + public synchronized void dispose() { + super.dispose(); + firstMap.removeMapChangeListener(firstMapListener); + firstMap = null; + secondMap = null; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/ComputedObservableMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/ComputedObservableMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.ComputedObservableMap; + +import java.lang.all; + +import java.util.AbstractSet; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; + +/** + * Maps objects to one of their attributes. Tracks changes to the underlying + * observable set of objects (keys), as well as changes to attribute values. + */ +public abstract class ComputedObservableMap : AbstractObservableMap { + + private final IObservableSet keySet; + + private ISetChangeListener setChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + Set addedKeys = new HashSet(event.diff.getAdditions()); + Set removedKeys = new HashSet(event.diff.getRemovals()); + Map oldValues = new HashMap(); + Map newValues = new HashMap(); + for (Iterator it = removedKeys.iterator(); it.hasNext();) { + Object removedKey = it.next(); + Object oldValue = doGet(removedKey); + unhookListener(removedKey); + if (oldValue !is null) { + oldValues.put(removedKey, oldValue); + } + } + for (Iterator it = addedKeys.iterator(); it.hasNext();) { + Object addedKey = it.next(); + hookListener(addedKey); + Object newValue = doGet(addedKey); + newValues.put(addedKey, newValue); + } + fireMapChange(Diffs.createMapDiff(addedKeys, removedKeys, + Collections.EMPTY_SET, oldValues, newValues)); + } + }; + + private Set entrySet = new EntrySet(); + + private class EntrySet : AbstractSet { + + public Iterator iterator() { + return new class() Iterator { + + final Iterator keyIterator; + this(){ + keyIterator = keySet.iterator(); + } + + public bool hasNext() { + return keyIterator.hasNext(); + } + + public Object next() { + return new class() Map.Entry { + Object key; + this(){ + key = keyIterator.next(); + } + + public Object getKey() { + return key; + } + + public Object getValue() { + return get(getKey()); + } + + public Object setValue(Object value) { + return put(getKey(), value); + } + }; + } + + public void remove() { + keyIterator.remove(); + } + }; + } + + public int size() { + return keySet.size(); + } + + } + + /** + * @param keySet + */ + public this(IObservableSet keySet) { + super(keySet.getRealm()); + this.keySet = keySet; + this.keySet.addSetChangeListener(setChangeListener); + } + + protected void init() { + for (Iterator it = this.keySet.iterator(); it.hasNext();) { + Object key = it.next(); + hookListener(key); + } + } + + protected final void fireSingleChange(Object key, Object oldValue, + Object newValue) { + fireMapChange(Diffs.createMapDiffSingleChange(key, oldValue, newValue)); + } + + public Set entrySet() { + return entrySet; + } + + public Set keySet() { + return keySet; + } + + final public Object get(Object key) { + return doGet(key); + } + + final public Object put(Object key, Object value) { + return doPut(key, value); + } + + /** + * @param removedKey + */ + protected abstract void unhookListener(Object removedKey); + + /** + * @param addedKey + */ + protected abstract void hookListener(Object addedKey); + + /** + * @param key + * @return the value for the given key + */ + protected abstract Object doGet(Object key); + + /** + * @param key + * @param value + * @return the old value for the given key + */ + protected abstract Object doPut(Object key, Object value); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/IMapChangeListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/IMapChangeListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.IMapChangeListener; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; + +/** + * Listener for changes to observable maps. + * + * @since 1.0 + * + */ +public interface IMapChangeListener : IObservablesListener { + + /** + * Handle a change an observable map. The given event object must only be + * used locally in this method because it may be reused for other change + * notifications. The diff object referenced by the event is immutable and + * may be used non-locally. + * + * @param event + * the event + */ + void handleMapChange(MapChangeEvent event); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/IObservableMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/IObservableMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.IObservableMap; + +import java.lang.all; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservable; + +/** + * Observable Map. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should instead subclass one of the classes that + * implement this interface. Note that direct implementers of this + * interface outside of the framework will be broken in future + * releases when methods are added to this interface. + * + * @see AbstractObservableMap + * @see ObservableMap + * + * @since 1.1 + */ +public interface IObservableMap : Map, IObservable { + + /** + * @param listener + */ + public void addMapChangeListener(IMapChangeListener listener); + + /** + * @param listener + */ + public void removeMapChangeListener(IMapChangeListener listener); + + /** + * @TrackedGetter + */ + public int size(); + + /** + * @TrackedGetter + */ + public bool isEmpty(); + + /** + * @TrackedGetter + */ + public bool containsKey(Object key); + + /** + * @TrackedGetter + */ + public bool containsValue(Object value); + + /** + * @TrackedGetter + */ + public Object get(Object key); + + /** + * + */ + public Object put(Object key, Object value); + + /** + * + */ + public Object remove(Object key); + + /** + * @TrackedGetter + */ + public Set keySet(); + + /** + * @TrackedGetter + */ + public Collection values(); + + /** + * @TrackedGetter + */ + public Set entrySet(); + + /** + * @TrackedGetter + */ + public bool opEquals(Object o); + + /** + * @TrackedGetter + */ + public int hashCode(); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/MapChangeEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/MapChangeEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.MapChangeEvent; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.ObservableEvent; + +/** + * Map change event describing an incremental change of an + * {@link IObservableMap} object. + * + * @since 1.0 + * + */ +public class MapChangeEvent : ObservableEvent { + + /** + * + */ + private static final long serialVersionUID = -8092347212410548463L; + static final Object TYPE = new Object(); + + /** + * Description of the change to the source observable map. Listeners must + * not change this field. + */ + public MapDiff diff; + + /** + * Creates a new map change event + * + * @param source + * the source observable map + * @param diff + * the map change + */ + public this(IObservableMap source, MapDiff diff) { + super(source); + this.diff = diff; + } + + /** + * Returns the observable map from which this event originated. + * + * @return the observable map from which this event originated + */ + public IObservableMap getObservableMap() { + return cast(IObservableMap) getSource(); + } + + protected void dispatch(IObservablesListener listener) { + (cast(IMapChangeListener) listener).handleMapChange(this); + } + + protected Object getListenerType() { + return TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/MapDiff.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/MapDiff.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.MapDiff; + +import java.lang.all; + +import java.util.Set; + +/** + * @since 1.1 + * + */ +public abstract class MapDiff { + + /** + * @return the set of keys which were added + */ + public abstract Set getAddedKeys(); + + /** + * @return the set of keys which were removed + */ + public abstract Set getRemovedKeys(); + + /** + * @return the set of keys for which the value has changed + */ + public abstract Set getChangedKeys(); + + /** + * Returns the old value for the given key, which must be an element of + * {@link #getRemovedKeys()} or {@link #getChangedKeys()}. + * + * @param key + * @return the old value for the given key. + */ + public abstract Object getOldValue(Object key); + + /** + * Returns the new value for the given key, which must be an element of + * {@link #getChangedKeys()} or {@link #getAddedKeys()}. + * + * @param key + * @return the new value for the given key. + */ + public abstract Object getNewValue(Object key); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/ObservableMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/ObservableMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.ObservableMap; + +import java.lang.all; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.AbstractObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; + +/** + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + */ +public class ObservableMap : AbstractObservable , IObservableMap { + + protected Map wrappedMap; + + private bool stale = false; + + /** + * @param wrappedMap + */ + public this(Map wrappedMap) { + this(Realm.getDefault(), wrappedMap); + } + + /** + * @param realm + * @param wrappedMap + */ + public this(Realm realm, Map wrappedMap) { + super(realm); + this.wrappedMap = wrappedMap; + } + + public synchronized void addMapChangeListener(IMapChangeListener listener) { + addListener(MapChangeEvent.TYPE, listener); + } + + public synchronized void removeMapChangeListener(IMapChangeListener listener) { + removeListener(MapChangeEvent.TYPE, listener); + } + + protected void getterCalled() { + ObservableTracker.getterCalled(this); + } + + protected void fireMapChange(MapDiff diff) { + checkRealm(); + + // fire general change event first + super.fireChange(); + + fireEvent(new MapChangeEvent(this, diff)); + } + + public bool containsKey(Object key) { + getterCalled(); + return wrappedMap.containsKey(key); + } + + public bool containsValue(Object value) { + getterCalled(); + return wrappedMap.containsValue(value); + } + + public Set entrySet() { + getterCalled(); + return wrappedMap.entrySet(); + } + + public Object get(Object key) { + getterCalled(); + return wrappedMap.get(key); + } + + public bool isEmpty() { + getterCalled(); + return wrappedMap.isEmpty(); + } + + public Set keySet() { + getterCalled(); + return wrappedMap.keySet(); + } + + public int size() { + getterCalled(); + return wrappedMap.size(); + } + + public Collection values() { + getterCalled(); + return wrappedMap.values(); + } + + /** + * Returns the stale state. Must be invoked from the current realm. + * + * @return stale state + */ + public bool isStale() { + checkRealm(); + return stale; + } + + /** + * Sets the stale state. Must be invoked from the current realm. + * + * @param stale + * The stale state to set. This will fire a stale event if the + * given bool is true and this observable set was not already + * stale. + */ + public void setStale(bool stale) { + checkRealm(); + bool wasStale = this.stale; + this.stale = stale; + if (!wasStale && stale) { + fireStale(); + } + } + + public Object put(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public void putAll(Map arg0) { + throw new UnsupportedOperationException(); + } + + public synchronized void dispose() { + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/WritableMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/WritableMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,119 @@ +/******************************************************************************* + * Copyright (c) 2006-2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Matthew Hall - bug 184830 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.map.WritableMap; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.internal.databinding.Util; + +/** + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + */ +public class WritableMap : ObservableMap { + + /** + * Constructs a new WritableMap on the default realm. + */ + public this() { + this(Realm.getDefault()); + } + + /** + * Constructs a new WritableMap on the given realm. + * + * @param realm + * the realm + */ + public this(Realm realm) { + super(realm, new HashMap()); + } + + /** + * Associates the provided value with the key. Must be invoked from the current realm. + */ + public Object put(Object key, Object value) { + checkRealm(); + Object result = wrappedMap.put(key, value); + if (!Util.equals(result, value)) { + if (resultisnull) { + fireMapChange(Diffs.createMapDiffSingleAdd(key, value)); + } else { + fireMapChange(Diffs.createMapDiffSingleChange(key, result, + value)); + } + } + return result; + } + + /** + * Removes the value with the provide key. Must be invoked from the current realm. + */ + public Object remove(Object key) { + checkRealm(); + Object result = wrappedMap.remove(key); + if (result !is null) { + fireMapChange(Diffs.createMapDiffSingleRemove(key, result)); + } + return result; + } + + /** + * Clears the map. Must be invoked from the current realm. + */ + public void clear() { + checkRealm(); + if (!isEmpty()) { + Map copy = new HashMap(wrappedMap); + wrappedMap.clear(); + fireMapChange(Diffs.createMapDiffRemoveAll(copy)); + } + } + + /** + * Adds the provided map's contents to this map. Must be invoked from the current realm. + */ + public void putAll(Map map) { + checkRealm(); + Set addedKeys = new HashSet(map.size()); + Map changes = new HashMap(map.size()); + for (Iterator it = map.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = cast(Entry) it.next(); + Object previousValue = wrappedMap.put(entry.getKey(), entry.getValue()); + if (previousValueisnull) { + addedKeys.add(entry.getKey()); + } else { + changes.put(entry.getKey(), previousValue); + } + } + if (!addedKeys.isEmpty() || !changes.isEmpty()) { + fireMapChange(Diffs.createMapDiff(addedKeys, Collections.EMPTY_SET, changes.keySet(), changes, wrappedMap)); + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides classes that can be used to observe changes in maps. +

+Package Specification

+

+This package provides classes that can be used to observe changes in maps.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/IObservableFactory.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/IObservableFactory.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservable; + +/** + * Generates an {@link IObservable} when passed a target instance. + * + * @since 1.0 + */ +public interface IObservableFactory { + + /** + * Creates an observable for the given target object. + * + * @param target + * @return the new observable + */ + public IObservable createObservable(Object target); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 147515 + * Matthew Hall - bug 221704 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableList; +import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableMap; +import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableSet; +import org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableValue; + +/** + * Allows for the observation of an attribute, the detail, of an observable + * representing selection or another transient instance, the master. + * + * @since 1.0 + */ +public class MasterDetailObservables { + + /** + * Creates a detail observable value from a master observable value and a + * factory. This can be used to create observable values that represent a + * property of a selected object in a table. + * + * @param master + * the observable value to track + * @param detailFactory + * a factory for creating {@link IObservableValue} instances + * given a current value of the master + * @param detailType + * the value type of the detail observable value, typically of + * type java.lang.Class and can be null + * @return an observable value of the given value type that, for any current + * value of the given master value, behaves like the observable + * value created by the factory for that current value. + */ + public static IObservableValue detailValue(IObservableValue master, + IObservableFactory detailFactory, Object detailType) { + return new DetailObservableValue(master, detailFactory, detailType); + } + + /** + * Creates a detail observable list from a master observable value and a + * factory. This can be used to create observable lists that represent a + * list property of a selected object in a table. + * + * @param master + * the observable value to track + * @param detailFactory + * a factory for creating {@link IObservableList} instances given + * a current value of the master + * @param detailElementType + * the element type of the detail observable list, typically of + * type java.lang.Class and can be null + * @return an observable list with the given element type that, for any + * current value of the given master value, behaves like the + * observable list created by the factory for that current value. + */ + public static IObservableList detailList(IObservableValue master, + IObservableFactory detailFactory, Object detailElementType) { + return new DetailObservableList(detailFactory, master, + detailElementType); + } + + /** + * Creates a detail observable set from a master observable value and a + * factory. This can be used to create observable sets that represent a set + * property of a selected object in a table. + * + * @param master + * the observable value to track + * @param detailFactory + * a factory for creating {@link IObservableSet} instances given + * a current value of the master + * @param detailElementType + * the element type of the detail observable set, typically of + * type java.lang.Class and can be null + * @return an observable set with the given element type that, for any + * current value of the given master value, behaves like the + * observable set created by the factory for that current value. + */ + public static IObservableSet detailSet(IObservableValue master, + IObservableFactory detailFactory, Object detailElementType) { + return new DetailObservableSet(detailFactory, master, detailElementType); + } + + /** + * Creates a detail observable map from a master observable value and a + * factory. This can be used to create observable maps that represent a map + * property of a selected object in a table. + * + * @param master + * the observable value to track + * @param detailFactory + * a factory for createing {@link IObservableMap} instances given + * a current value of the master + * @return an observable map that, for any current value of the given master + * value, behaves like the observable map created by the factory for + * that current value. + * @since 1.1 + */ + public static IObservableMap detailMap(IObservableValue master, + IObservableFactory detailFactory) { + return new DetailObservableMap(detailFactory, master); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,17 @@ + + + + + + + Package-level Javadoc + + +Provides classes that can be used to observe a detail of a master object. +

+Package Specification

+

+This package provides classes that can be used to observe a detail of a master object. +A common use case for master detail is observing the detail (e.g. name) of a master (e.g. selected Person) of a list of elements.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides the core APIs for observing changes in objects. +

+Package Specification

+

+This package provides the core APIs for observing changes in objects.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/AbstractObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/AbstractObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.AbstractObservableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.AbstractObservable; +import org.eclipse.core.databinding.observable.ChangeSupport; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; + +/** + * + * Abstract implementation of {@link IObservableSet}. + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + */ +public abstract class AbstractObservableSet : AbstractObservable , + IObservableSet { + + private ChangeSupport changeSupport; + + private bool stale = false; + + protected this() { + this(Realm.getDefault()); + } + + protected void firstListenerAdded() { + super.firstListenerAdded(); + } + + protected void lastListenerRemoved() { + super.lastListenerRemoved(); + } + + protected this(Realm realm) { + super(realm); + changeSupport = new class(realm) ChangeSupport { + protected void firstListenerAdded() { + this.outer.firstListenerAdded(); + } + protected void lastListenerRemoved() { + this.outer.lastListenerRemoved(); + } + }; + } + + public synchronized void addSetChangeListener(ISetChangeListener listener) { + changeSupport.addListener(SetChangeEvent.TYPE, listener); + } + + public synchronized void removeSetChangeListener(ISetChangeListener listener) { + changeSupport.removeListener(SetChangeEvent.TYPE, listener); + } + + protected abstract Set getWrappedSet(); + + protected void fireSetChange(SetDiff diff) { + // fire general change event first + super.fireChange(); + + changeSupport.fireEvent(new SetChangeEvent(this, diff)); + } + + public bool contains(Object o) { + getterCalled(); + return getWrappedSet().contains(o); + } + + public bool containsAll(Collection c) { + getterCalled(); + return getWrappedSet().containsAll(c); + } + + public override bool opEquals(Object o) { + getterCalled(); + return getWrappedSet().equals(o); + } + + public int hashCode() { + getterCalled(); + return getWrappedSet().hashCode(); + } + + public bool isEmpty() { + getterCalled(); + return getWrappedSet().isEmpty(); + } + + public Iterator iterator() { + getterCalled(); + final Iterator wrappedIterator = getWrappedSet().iterator(); + return new class() Iterator { + + public void remove() { + throw new UnsupportedOperationException(); + } + + public bool hasNext() { + ObservableTracker.getterCalled(this.outer); + return wrappedIterator.hasNext(); + } + + public Object next() { + ObservableTracker.getterCalled(this.outer); + return wrappedIterator.next(); + } + }; + } + + public int size() { + getterCalled(); + return getWrappedSet().size(); + } + + public Object[] toArray() { + getterCalled(); + return getWrappedSet().toArray(); + } + + public Object[] toArray(Object[] a) { + getterCalled(); + return getWrappedSet().toArray(a); + } + + public String toString() { + getterCalled(); + return getWrappedSet().toString(); + } + + protected void getterCalled() { + ObservableTracker.getterCalled(this); + } + + public bool add(Object o) { + throw new UnsupportedOperationException(); + } + + public bool addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool remove(Object o) { + throw new UnsupportedOperationException(); + } + + public bool removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * @return Returns the stale state. + */ + public bool isStale() { + getterCalled(); + return stale; + } + + /** + * @param stale + * The stale state to set. This will fire a stale event if the + * given bool is true and this observable set was not already + * stale. + */ + public void setStale(bool stale) { + checkRealm(); + bool wasStale = this.stale; + this.stale = stale; + if (!wasStale && stale) { + fireStale(); + } + } + + + protected void fireChange() { + throw new RuntimeException("fireChange should not be called, use fireSetChange() instead"); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.jface.provisional.databinding.observable.AbstractObservable#dispose() + */ + public synchronized void dispose() { + super.dispose(); + + if (changeSupport !is null) { + changeSupport.dispose(); + changeSupport = null; + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/IObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/IObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.IObservableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservableCollection; + +/** + * A set whose changes can be tracked by set change listeners. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should instead subclass one of the classes that + * implement this interface. Note that direct implementers of this + * interface outside of the framework will be broken in future + * releases when methods are added to this interface. + * + * @see AbstractObservableSet + * @see ObservableSet + * + * @since 1.0 + * + */ +public interface IObservableSet : Set, IObservableCollection { + + /** + * @param listener + */ + public void addSetChangeListener(ISetChangeListener listener); + + /** + * @param listener + */ + public void removeSetChangeListener(ISetChangeListener listener); + + /** + * @return the element type or null if untyped + */ + public Object getElementType(); + + /** + * @TrackedGetter + */ + int size(); + + /** + * @TrackedGetter + */ + bool isEmpty(); + + /** + * @TrackedGetter + */ + bool contains(Object o); + + /** + * @TrackedGetter + */ + Iterator iterator(); + + /** + * @TrackedGetter + */ + Object[] toArray(); + + /** + * @TrackedGetter + */ + Object[] toArray(Object a[]); + + // Modification Operations + + /** + * @TrackedGetter + */ + bool add(Object o); + + /** + * @TrackedGetter + */ + bool remove(Object o); + + // Bulk Operations + + /** + * @TrackedGetter + */ + bool containsAll(Collection c); + + /** + * @TrackedGetter + */ + bool addAll(Collection c); + + /** + * @TrackedGetter + */ + bool retainAll(Collection c); + + /** + * @TrackedGetter + */ + bool removeAll(Collection c); + + // Comparison and hashing + + /** + * @TrackedGetter + */ + bool opEquals(Object o); + + /** + * @TrackedGetter + */ + int hashCode(); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ISetChangeListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ISetChangeListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.ISetChangeListener; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; + +/** + * Listener for changes to observable sets. + * + * @since 1.0 + * + */ +public interface ISetChangeListener : IObservablesListener { + + /** + * Handle a change to an observable set. The given event object must only be + * used locally in this method because it may be reused for other change + * notifications. The diff object referenced by the event is immutable and + * may be used non-locally. + * + * @param event + * the event + */ + void handleSetChange(SetChangeEvent event); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ListToSetAdapter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ListToSetAdapter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.ListToSetAdapter; + +import java.lang.all; + +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; + +/** + * Observable set backed by an observable list. The wrapped list must not + * contain duplicate elements. + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + * + */ +public class ListToSetAdapter : ObservableSet { + + private final IObservableList list; + + private IListChangeListener listener = new class() IListChangeListener { + + public void handleListChange(ListChangeEvent event) { + Set added = new HashSet(); + Set removed = new HashSet(); + ListDiffEntry[] differences = event.diff.getDifferences(); + for (int i = 0; i < differences.length; i++) { + ListDiffEntry entry = differences[i]; + Object element = entry.getElement(); + if (entry.isAddition()) { + if (wrappedSet.add(element)) { + if (!removed.remove(element)) + added.add(element); + } + } else { + if (wrappedSet.remove(element)) { + removed.add(element); + added.remove(element); + } + } + } + fireSetChange(Diffs.createSetDiff(added, removed)); + } + }; + + /** + * @param list + */ + public this(IObservableList list) { + super(list.getRealm(), new HashSet(), list.getElementType()); + this.list = list; + wrappedSet.addAll(list); + this.list.addListChangeListener(listener); + } + + public synchronized void dispose() { + super.dispose(); + if (list !is null && listener !is null) { + list.removeListChangeListener(listener); + listener = null; + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/MappedSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/MappedSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.MappedSet; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.map.IMapChangeListener; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.map.MapChangeEvent; +import org.eclipse.core.databinding.observable.map.MapDiff; + +/** + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + * + */ +public class MappedSet : ObservableSet { + + private final IObservableMap wrappedMap; + + /* + * Map from values (range elements) to Integer ref counts + */ + private Map valueCounts = new HashMap(); + + private ISetChangeListener domainListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + Set additions = new HashSet(); + for (Iterator it = event.diff.getAdditions().iterator(); it.hasNext();) { + Object added = it.next(); + Object mapValue = wrappedMap.get(added); + if (handleAddition(mapValue)) { + additions.add(mapValue); + } + } + Set removals = new HashSet(); + for (Iterator it = event.diff.getRemovals().iterator(); it.hasNext();) { + Object removed = it.next(); + Object mapValue = wrappedMap.get(removed); + if (handleRemoval(mapValue)) { + removals.add(mapValue); + } + } + fireSetChange(Diffs.createSetDiff(additions, removals)); + } + }; + + private IMapChangeListener mapChangeListener = new class() IMapChangeListener { + public void handleMapChange(MapChangeEvent event) { + MapDiff diff = event.diff; + Set additions = new HashSet(); + Set removals = new HashSet(); + for (Iterator it = diff.getRemovedKeys().iterator(); it.hasNext();) { + Object key = it.next(); + Object oldValue = diff.getOldValue(key); + if (handleRemoval(oldValue)) { + removals.add(oldValue); + } + } + for (Iterator it = diff.getChangedKeys().iterator(); it.hasNext();) { + Object key = it.next(); + Object oldValue = diff.getOldValue(key); + Object newValue = diff.getNewValue(key); + if (handleRemoval(oldValue)) { + removals.add(oldValue); + } + if (handleAddition(newValue)) { + additions.add(newValue); + } + } + for (Iterator it = diff.getAddedKeys().iterator(); it.hasNext();) { + Object key = it.next(); + Object newValue = diff.getNewValue(key); + if (handleAddition(newValue)) { + additions.add(newValue); + } + } + fireSetChange(Diffs.createSetDiff(additions, removals)); + } + }; + + private IObservableSet input; + + /** + * @param input + * @param map + */ + public this(IObservableSet input, IObservableMap map) { + super(input.getRealm(), Collections.EMPTY_SET, Object.classinfo); + setWrappedSet(valueCounts.keySet()); + this.wrappedMap = map; + this.input = input; + for (Iterator it = input.iterator(); it.hasNext();) { + Object element = it.next(); + Object functionValue = wrappedMap.get(element); + handleAddition(functionValue); + } + input.addSetChangeListener(domainListener); + map.addMapChangeListener(mapChangeListener); + } + + /** + * @param mapValue + * @return true if the given mapValue was an addition + */ + protected bool handleAddition(Object mapValue) { + Integer count = cast(Integer) valueCounts.get(mapValue); + if (count is null) { + valueCounts.put(mapValue, new Integer(1)); + return true; + } + valueCounts.put(mapValue, new Integer(count.intValue() + 1)); + return false; + } + + /** + * @param mapValue + * @return true if the given mapValue has been removed + */ + protected bool handleRemoval(Object mapValue) { + Integer count = cast(Integer) valueCounts.get(mapValue); + if (count.intValue() <= 1) { + valueCounts.remove(mapValue); + return true; + } + valueCounts.put(mapValue, new Integer(count.intValue() - 1)); + return false; + } + + public synchronized void dispose() { + wrappedMap.removeMapChangeListener(mapChangeListener); + input.removeSetChangeListener(domainListener); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,210 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.ObservableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.AbstractObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; + +/** + * + * Abstract implementation of {@link IObservableSet}. + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + * + */ +public abstract class ObservableSet : AbstractObservable , + IObservableSet { + + protected Set wrappedSet; + + private bool stale = false; + + protected Object elementType; + + protected this(Set wrappedSet, Object elementType) { + this(Realm.getDefault(), wrappedSet, elementType); + } + + protected this(Realm realm, Set wrappedSet, Object elementType) { + super(realm); + this.wrappedSet = wrappedSet; + this.elementType = elementType; + } + + public synchronized void addSetChangeListener(ISetChangeListener listener) { + addListener(SetChangeEvent.TYPE, listener); + } + + public synchronized void removeSetChangeListener(ISetChangeListener listener) { + removeListener(SetChangeEvent.TYPE, listener); + } + + protected void fireSetChange(SetDiff diff) { + // fire general change event first + super.fireChange(); + + fireEvent(new SetChangeEvent(this, diff)); + } + + public bool contains(Object o) { + getterCalled(); + return wrappedSet.contains(o); + } + + public bool containsAll(Collection c) { + getterCalled(); + return wrappedSet.containsAll(c); + } + + public override bool opEquals(Object o) { + getterCalled(); + return wrappedSet.equals(o); + } + + public int hashCode() { + getterCalled(); + return wrappedSet.hashCode(); + } + + public bool isEmpty() { + getterCalled(); + return wrappedSet.isEmpty(); + } + + public Iterator iterator() { + getterCalled(); + final Iterator wrappedIterator = wrappedSet.iterator(); + return new class() Iterator { + + public void remove() { + throw new UnsupportedOperationException(); + } + + public bool hasNext() { + ObservableTracker.getterCalled(this.outer); + return wrappedIterator.hasNext(); + } + + public Object next() { + ObservableTracker.getterCalled(this.outer); + return wrappedIterator.next(); + } + }; + } + + public int size() { + getterCalled(); + return wrappedSet.size(); + } + + public Object[] toArray() { + getterCalled(); + return wrappedSet.toArray(); + } + + public Object[] toArray(Object[] a) { + getterCalled(); + return wrappedSet.toArray(a); + } + + public String toString() { + getterCalled(); + return wrappedSet.toString(); + } + + protected void getterCalled() { + ObservableTracker.getterCalled(this); + } + + public bool add(Object o) { + throw new UnsupportedOperationException(); + } + + public bool addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool remove(Object o) { + throw new UnsupportedOperationException(); + } + + public bool removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + /** + * @return Returns the stale state. + */ + public bool isStale() { + getterCalled(); + return stale; + } + + /** + * @param stale + * The stale state to set. This will fire a stale event if the + * given bool is true and this observable set was not already + * stale. + */ + public void setStale(bool stale) { + checkRealm(); + bool wasStale = this.stale; + this.stale = stale; + if (!wasStale && stale) { + fireStale(); + } + } + + /** + * @param wrappedSet The wrappedSet to set. + */ + protected void setWrappedSet(Set wrappedSet) { + this.wrappedSet = wrappedSet; + } + + protected void fireChange() { + throw new RuntimeException("fireChange should not be called, use fireSetChange() instead"); //$NON-NLS-1$ + } + + /* (non-Javadoc) + * @see org.eclipse.jface.provisional.databinding.observable.AbstractObservable#dispose() + */ + public synchronized void dispose() { + super.dispose(); + } + + public Object getElementType() { + return elementType; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/SetChangeEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/SetChangeEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.SetChangeEvent; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.ObservableEvent; + +/** + * List change event describing an incremental change of an + * {@link IObservableSet} object. + * + * @since 1.0 + * + */ +public class SetChangeEvent : ObservableEvent { + + /** + * + */ + private static final long serialVersionUID = 7436547103857482256L; + static final Object TYPE = new Object(); + + /** + * Description of the change to the source observable set. Listeners must + * not change this field. + */ + public SetDiff diff; + + /** + * Creates a new set change event. + * + * @param source + * the source observable set + * @param diff + * the set change + */ + public this(IObservableSet source, SetDiff diff) { + super(source); + this.diff = diff; + } + + /** + * Returns the observable set from which this event originated. + * + * @return the observable set from which this event originated + */ + public IObservableSet getObservableSet() { + return cast(IObservableSet) getSource(); + } + + protected void dispatch(IObservablesListener listener) { + (cast(ISetChangeListener) listener).handleSetChange(this); + } + + protected Object getListenerType() { + return TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/SetDiff.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/SetDiff.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.SetDiff; + +import java.lang.all; + +import java.util.Set; + +/** + * @since 1.0 + * + */ +public abstract class SetDiff { + + /** + * @return the set of added elements + */ + public abstract Set getAdditions(); + + /** + * @return the set of removed elements + */ + public abstract Set getRemovals(); + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer + .append(getClass().getName()) + .append("{additions [") //$NON-NLS-1$ + .append(getAdditions() !is null ? getAdditions().toString() : "null") //$NON-NLS-1$ + .append("], removals [") //$NON-NLS-1$ + .append(getRemovals() !is null ? getRemovals().toString() : "null") //$NON-NLS-1$ + .append("]}"); //$NON-NLS-1$ + + return buffer.toString(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/UnionSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/UnionSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.UnionSet; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.internal.databinding.observable.IStalenessConsumer; +import org.eclipse.core.internal.databinding.observable.StalenessTracker; + +/** + * Represents a set consisting of the union of elements from one or more other + * sets. This object does not need to be explicitly disposed. If nobody is + * listening to the UnionSet, the set will remove its listeners. + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + */ +public final class UnionSet : ObservableSet { + + /** + * child sets + */ + private IObservableSet[] childSets; + + private bool stale = false; + + /** + * Map of elements onto Integer reference counts. This map is constructed + * when the first listener is added to the union set. Null if nobody is + * listening to the UnionSet. + */ + private HashMap refCounts = null; + + private StalenessTracker stalenessTracker; + + /** + * @param childSets + */ + public this(IObservableSet[] childSets) { + super(childSets[0].getRealm(), null, childSets[0].getElementType()); + System.arraycopy(childSets, 0, this.childSets = new IObservableSet[childSets.length], 0, childSets.length); + this.stalenessTracker = new StalenessTracker(childSets, + stalenessConsumer); + } + + private ISetChangeListener childSetChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + processAddsAndRemoves(event.diff.getAdditions(), event.diff.getRemovals()); + } + }; + + private IStalenessConsumer stalenessConsumer = new class() IStalenessConsumer { + public void setStale(bool stale) { + bool oldStale = this.outer.stale; + this.outer.stale = stale; + if (stale && !oldStale) { + fireStale(); + } + } + }; + + public bool isStale() { + getterCalled(); + if (refCounts !is null) { + return stale; + } + + for (int i = 0; i < childSets.length; i++) { + IObservableSet childSet = childSets[i]; + + if (childSet.isStale()) { + return true; + } + } + return false; + } + + private void processAddsAndRemoves(Set adds, Set removes) { + Set addsToFire = new HashSet(); + Set removesToFire = new HashSet(); + + for (Iterator iter = adds.iterator(); iter.hasNext();) { + Object added = iter.next(); + + Integer refCount = cast(Integer) refCounts.get(added); + if (refCount is null) { + refCounts.put(added, new Integer(1)); + addsToFire.add(added); + } else { + int refs = refCount.intValue(); + refCount = new Integer(refs + 1); + refCounts.put(added, refCount); + } + } + + for (Iterator iter = removes.iterator(); iter.hasNext();) { + Object removed = iter.next(); + + Integer refCount = cast(Integer) refCounts.get(removed); + if (refCount !is null) { + int refs = refCount.intValue(); + if (refs <= 1) { + removesToFire.add(removed); + refCounts.remove(removed); + } else { + refCount = new Integer(refCount.intValue() - 1); + refCounts.put(removed, refCount); + } + } + } + + // just in case the removes overlapped with the adds + addsToFire.removeAll(removesToFire); + + if (addsToFire.size() > 0 || removesToFire.size() > 0) { + fireSetChange(Diffs.createSetDiff(addsToFire, removesToFire)); + } + } + + protected void firstListenerAdded() { + super.firstListenerAdded(); + + refCounts = new HashMap(); + for (int i = 0; i < childSets.length; i++) { + IObservableSet next = childSets[i]; + next.addSetChangeListener(childSetChangeListener); + incrementRefCounts(next); + } + stalenessTracker = new StalenessTracker(childSets, stalenessConsumer); + setWrappedSet(refCounts.keySet()); + } + + protected void lastListenerRemoved() { + super.lastListenerRemoved(); + + for (int i = 0; i < childSets.length; i++) { + IObservableSet next = childSets[i]; + + next.removeSetChangeListener(childSetChangeListener); + stalenessTracker.removeObservable(next); + } + refCounts = null; + stalenessTracker = null; + setWrappedSet(null); + } + + private ArrayList incrementRefCounts(Collection added) { + ArrayList adds = new ArrayList(); + + for (Iterator iter = added.iterator(); iter.hasNext();) { + Object next = iter.next(); + + Integer refCount = cast(Integer) refCounts.get(next); + if (refCount is null) { + adds.add(next); + refCount = new Integer(1); + refCounts.put(next, refCount); + } else { + refCount = new Integer(refCount.intValue() + 1); + refCounts.put(next, refCount); + } + } + return adds; + } + + protected void getterCalled() { + super.getterCalled(); + if (refCounts is null) { + // no listeners, recompute + setWrappedSet(computeElements()); + } + } + + private Set computeElements() { + // If there is no cached value, compute the union from scratch + if (refCounts is null) { + Set result = new HashSet(); + for (int i = 0; i < childSets.length; i++) { + result.addAll(childSets[i]); + } + return result; + } + + // Else there is a cached value. Return it. + return refCounts.keySet(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/WritableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/WritableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2006-2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 147515 + * Matthew Hall - bug 221351 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.set.WritableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; + +/** + * Mutable (writable) implementation of {@link IObservableSet}. + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + */ +public class WritableSet : ObservableSet { + + /** + * Constructs a new empty instance in the default realm with a + * null element type. + * + */ + public this() { + this(Realm.getDefault()); + } + + /** + * Constructs a new instance in the default realm containing the + * elements of the given collection. Changes to the given collection after + * calling this method do not affect the contents of the created WritableSet. + * + * @param c + * @param elementType + * can be null + */ + public this(Collection c, Object elementType) { + this(Realm.getDefault(), new HashSet(c), elementType); + } + + /** + * Constructs a new empty instance in the given realm and a + * null element type. + * + * @param realm + */ + public this(Realm realm) { + this(realm, new HashSet(), null); + } + + /** + * Constructs a new instance in the default realm with the given element + * type, containing the elements of the given collection. Changes to the + * given collection after calling this method do not affect the contents of + * the created WritableSet. + * + * @param realm + * @param c + * @param elementType + * can be null + */ + public this(Realm realm, Collection c, Object elementType) { + super(realm, new HashSet(c), elementType); + this.elementType = elementType; + } + + public bool add(Object o) { + getterCalled(); + bool added = wrappedSet.add(o); + if (added) { + fireSetChange(Diffs.createSetDiff(Collections.singleton(o), Collections.EMPTY_SET)); + } + return added; + } + + public bool addAll(Collection c) { + getterCalled(); + Set additions = new HashSet(); + Iterator it = c.iterator(); + while (it.hasNext()) { + Object element = it.next(); + if (wrappedSet.add(element)) { + additions.add(element); + } + } + if (additions.size() > 0) { + fireSetChange(Diffs.createSetDiff(additions, Collections.EMPTY_SET)); + return true; + } + return false; + } + + public bool remove(Object o) { + getterCalled(); + bool removed = wrappedSet.remove(o); + if (removed) { + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, Collections + .singleton(o))); + } + return removed; + } + + public bool removeAll(Collection c) { + getterCalled(); + Set removes = new HashSet(); + Iterator it = c.iterator(); + while (it.hasNext()) { + Object element = it.next(); + if (wrappedSet.remove(element)) { + removes.add(element); + } + } + if (removes.size() > 0) { + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removes)); + return true; + } + return false; + } + + public bool retainAll(Collection c) { + getterCalled(); + Set removes = new HashSet(); + Iterator it = wrappedSet.iterator(); + while (it.hasNext()) { + Object element = it.next(); + if (!c.contains(element)) { + it.remove(); + removes.add(element); + } + } + if (removes.size() > 0) { + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removes)); + return true; + } + return false; + } + + public void clear() { + getterCalled(); + Set removes = new HashSet(wrappedSet); + wrappedSet.clear(); + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removes)); + } + + /** + * @param elementType can be null + * @return new instance with the default realm + */ + public static WritableSet withElementType(Object elementType) { + return new WritableSet(Realm.getDefault(), new HashSet(), elementType); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides classes that can be used to observe changes in sets. +

+Package Specification

+

+This package provides classes that can be used to observe changes in sets.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/AbstractObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/AbstractObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.value.AbstractObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.AbstractObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; + +/** + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + * + */ +abstract public class AbstractObservableValue : AbstractObservable , IObservableValue { + /** + * Constructs a new instance with the default realm. + */ + public this() { + this(Realm.getDefault()); + } + + /** + * @param realm + */ + public this(Realm realm) { + super(realm); + } + + public synchronized void addValueChangeListener(IValueChangeListener listener) { + addListener(ValueChangeEvent.TYPE, listener); + } + + public synchronized void removeValueChangeListener(IValueChangeListener listener) { + removeListener(ValueChangeEvent.TYPE, listener); + } + + final public void setValue(Object value) { + checkRealm(); + doSetValue(value); + } + + /** + * Template method for setting the value of the observable. By default the + * method throws an {@link UnsupportedOperationException}. + * + * @param value + */ + protected void doSetValue(Object value) { + throw new UnsupportedOperationException(); + } + + protected void fireValueChange(ValueDiff diff) { + // fire general change event first + super.fireChange(); + fireEvent(new ValueChangeEvent(this, diff)); + } + + public final Object getValue() { + getterCalled(); + return doGetValue(); + } + + abstract protected Object doGetValue(); + + public bool isStale() { + getterCalled(); + return false; + } + + private void getterCalled() { + ObservableTracker.getterCalled(this); + } + + protected void fireChange() { + throw new RuntimeException( + "fireChange should not be called, use fireValueChange() instead"); //$NON-NLS-1$ + } + + public synchronized void dispose() { + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/AbstractVetoableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/AbstractVetoableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,97 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + *******************************************************************************/ +module org.eclipse.core.databinding.observable.value.AbstractVetoableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.internal.databinding.Util; + +/** + * + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + * + */ +public abstract class AbstractVetoableValue : AbstractObservableValue + , IVetoableValue { + + /** + * Creates a new vetoable value. + */ + public this() { + this(Realm.getDefault()); + } + + /** + * @param realm + */ + public this(Realm realm) { + super(realm); + } + + final protected void doSetValue(Object value) { + Object currentValue = doGetValue(); + ValueDiff diff = Diffs.createValueDiff(currentValue, value); + bool okToProceed = fireValueChanging(diff); + if (!okToProceed) { + throw new ChangeVetoException("Change not permitted"); //$NON-NLS-1$ + } + doSetApprovedValue(value); + + if (!Util.equals(diff.getOldValue(), diff.getNewValue())) { + fireValueChange(diff); + } + } + + /** + * Sets the value. Invoked after performing veto checks. Should not fire change events. + * + * @param value + */ + protected abstract void doSetApprovedValue(Object value); + + public synchronized void addValueChangingListener( + IValueChangingListener listener) { + addListener(ValueChangingEvent.TYPE, listener); + } + + public synchronized void removeValueChangingListener( + IValueChangingListener listener) { + removeListener(ValueChangingEvent.TYPE, listener); + } + + /** + * Notifies listeners about a pending change, and returns true if no + * listener vetoed the change. + * + * @param diff + * @return false if the change was vetoed, true otherwise + */ + protected bool fireValueChanging(ValueDiff diff) { + checkRealm(); + + ValueChangingEvent event = new ValueChangingEvent(this, diff); + fireEvent(event); + return !event.veto; + } + + public synchronized void dispose() { + super.dispose(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ChangeVetoException.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ChangeVetoException.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2005, 2006 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.observable.value.ChangeVetoException; + +import java.lang.all; + +/** + * @since 1.0 + * + */ +public class ChangeVetoException : RuntimeException { + + /** + * @param string + */ + public this(String string) { + super(string); + } + + private static final long serialVersionUID = 1L; + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ComputedValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ComputedValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,263 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 116920 + * Brad Reynolds - bug 147515 + *******************************************************************************/ +module org.eclipse.core.databinding.observable.value.ComputedValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.StaleEvent; + +/** + * A Lazily calculated value that automatically computes and registers listeners + * on its dependencies as long as all of its dependencies are IObservable + * objects + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * + * @since 1.0 + */ +public abstract class ComputedValue : AbstractObservableValue { + + private bool dirty = true; + + private bool stale = false; + + private Object cachedValue = null; + + /** + * Array of observables this computed value depends on. This field has a + * value of null if we are not currently listening. + */ + private IObservable[] dependencies = null; + + /** + * + */ + public this() { + this(Realm.getDefault(), null); + } + + /** + * @param valueType + * can be null + */ + public this(Object valueType) { + this(Realm.getDefault(), valueType); + } + + /** + * @param realm + * + */ + public this(Realm realm) { + this(realm, null); + } + + /** + * @param realm + * @param valueType + */ + public this(Realm realm, Object valueType) { + super(realm); + this.valueType = valueType; + } + + /** + * Inner class that implements interfaces that we don't want to expose as + * public API. Each interface could have been implemented using a separate + * anonymous class, but we combine them here to reduce the memory overhead + * and number of classes. + * + *

+ * The Runnable calls computeValue and stores the result in cachedValue. + *

+ * + *

+ * The IChangeListener stores each observable in the dependencies list. This + * is registered as the listener when calling ObservableTracker, to detect + * every observable that is used by computeValue. + *

+ * + *

+ * The IChangeListener is attached to every dependency. + *

+ * + */ + private class PrivateInterface : Runnable, IChangeListener, + IStaleListener { + public void run() { + cachedValue = calculate(); + } + + public void handleStale(StaleEvent event) { + if (!dirty && !stale) { + stale = true; + fireStale(); + } + } + + public void handleChange(ChangeEvent event) { + makeDirty(); + } + } + + private PrivateInterface privateInterface = new PrivateInterface(); + + private Object valueType; + + protected final Object doGetValue() { + if (dirty) { + // This line will do the following: + // - Run the calculate method + // - While doing so, add any observable that is touched to the + // dependencies list + IObservable[] newDependencies = ObservableTracker.runAndMonitor( + privateInterface, privateInterface, null); + + stale = false; + for (int i = 0; i < newDependencies.length; i++) { + IObservable observable = newDependencies[i]; + // Add a change listener to the new dependency. + if (observable.isStale()) { + stale = true; + } else { + observable.addStaleListener(privateInterface); + } + } + + dependencies = newDependencies; + + dirty = false; + } + + return cachedValue; + } + + /** + * Subclasses must override this method to provide the object's value. + * + * @return the object's value + */ + protected abstract Object calculate(); + + protected final void makeDirty() { + if (!dirty) { + dirty = true; + + stopListening(); + + // copy the old value + final Object oldValue = cachedValue; + // Fire the "dirty" event. This implementation recomputes the new + // value lazily. + fireValueChange(new class() ValueDiff { + + public Object getOldValue() { + return oldValue; + } + + public Object getNewValue() { + return getValue(); + } + }); + } + } + + /** + * + */ + private void stopListening() { + // Stop listening for dependency changes. + if (dependencies !is null) { + for (int i = 0; i < dependencies.length; i++) { + IObservable observable = dependencies[i]; + + observable.removeChangeListener(privateInterface); + observable.removeStaleListener(privateInterface); + } + dependencies = null; + } + } + + public bool isStale() { + // we need to recompute, otherwise staleness wouldn't mean anything + getValue(); + return stale; + } + + public Object getValueType() { + return valueType; + } + + // this method exists here so that we can call it from the runnable below. + /** + * @since 1.1 + */ + protected bool hasListeners() { + return super.hasListeners(); + } + + public synchronized void addChangeListener(IChangeListener listener) { + super.addChangeListener(listener); + // If somebody is listening, we need to make sure we attach our own + // listeners + computeValueForListeners(); + } + + /** + * Some clients just add a listener and expect to get notified even if they + * never called getValue(), so we have to call getValue() ourselves here to + * be sure. Need to be careful about realms though, this method can be + * called outside of our realm. See also bug 198211. If a client calls this + * outside of our realm, they may receive change notifications before the + * runnable below has been executed. It is their job to figure out what to + * do with those notifications. + */ + private void computeValueForListeners() { + getRealm().exec(new class() Runnable { + public void run() { + if (dependencies is null) { + // We are not currently listening. + if (hasListeners()) { + // But someone is listening for changes. Call getValue() + // to make sure we start listening to the observables we + // depend on. + getValue(); + } + } + } + }); + } + + public synchronized void addValueChangeListener( + IValueChangeListener listener) { + super.addValueChangeListener(listener); + // If somebody is listening, we need to make sure we attach our own + // listeners + computeValueForListeners(); + } + + public synchronized void dispose() { + super.dispose(); + stopListening(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.value.IObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.Realm; + +/** + * A value whose changes can be tracked by value change listeners. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should instead subclass one of the classes that + * implement this interface. Note that direct implementers of this + * interface outside of the framework will be broken in future + * releases when methods are added to this interface. + * + * @see AbstractObservableValue + * + * @since 1.0 + */ +public interface IObservableValue : IObservable { + + /** + * The value type of this observable value, or null if this + * observable value is untyped. + * + * @return the value type, or null + */ + public Object getValueType(); + + /** + * Returns the value. Must be invoked in the {@link Realm} of the observable. + * + * @return the current value + * @TrackedGetter + */ + public Object getValue(); + + /** + * Sets the value. Must be invoked in the {@link Realm} of the observable. + * + * @param value + * the value to set + * @throws UnsupportedOperationException + * if this observable value cannot be set. + */ + public void setValue(Object value); + + /** + * + * @param listener + */ + public void addValueChangeListener(IValueChangeListener listener); + + /** + * @param listener + */ + public void removeValueChangeListener(IValueChangeListener listener); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IValueChangeListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IValueChangeListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.value.IValueChangeListener; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; + +/** + * Listener for changes to observable values. + * + * @since 1.0 + * + */ +public interface IValueChangeListener : IObservablesListener { + + /** + * Handles a change to an observable value. The given event object must only + * be used locally in this method because it may be reused for other change + * notifications. The diff object referenced by the event is immutable and + * may be used non-locally. + * + * @param event + * the event + */ + void handleValueChange(ValueChangeEvent event); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IValueChangingListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IValueChangingListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.observable.value.IValueChangingListener; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; + +/** + * Listener for pre-change events for observable values. + * + * @since 1.0 + * + */ +public interface IValueChangingListener : IObservablesListener { + + /** + * This method is called when the value is about to change and provides an + * opportunity to veto the change. The given event object must only be used + * locally in this method because it may be reused for other change + * notifications. The diff object referenced by the event is immutable and + * may be used non-locally. + * + * @param event + */ + public void handleValueChanging(ValueChangingEvent event); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IVetoableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IVetoableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.observable.value.IVetoableValue; + +import java.lang.all; + +/** + * An observable value whose changes can be vetoed by listeners. + * + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + * Clients should instead subclass one of the classes that + * implement this interface. Note that direct implementers of this + * interface outside of the framework will be broken in future + * releases when methods are added to this interface. + * + * @since 1.0 + * + */ +public interface IVetoableValue : IObservableValue { + + /** + * @param listener + */ + public void addValueChangingListener(IValueChangingListener listener); + + /** + * @param listener + */ + public void removeValueChangingListener(IValueChangingListener listener); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueChangeEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueChangeEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.value.ValueChangeEvent; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.ObservableEvent; + +/** + * Value change event describing a change of an {@link IObservableValue} + * object's current value. + * + * @since 1.0 + * + */ +public class ValueChangeEvent : ObservableEvent { + + /** + * + */ + private static final long serialVersionUID = 2305345286999701156L; + + static final Object TYPE = new Object(); + + /** + * Description of the change to the source observable value. Listeners must + * not change this field. + */ + public ValueDiff diff; + + /** + * Creates a new value change event. + * + * @param source + * the source observable value + * @param diff + * the value change + */ + public this(IObservableValue source, ValueDiff diff) { + super(source); + this.diff = diff; + } + + /** + * Returns the observable value from which this event originated. + * + * @return returns the observable value from which this event originated + */ + public IObservableValue getObservableValue() { + return cast(IObservableValue) source; + } + + protected void dispatch(IObservablesListener listener) { + (cast(IValueChangeListener) listener).handleValueChange(this); + } + + protected Object getListenerType() { + return TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueChangingEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueChangingEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.databinding.observable.value.ValueChangingEvent; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.ObservableEvent; + +/** + * Value changing event describing a pending change of an + * {@link IObservableValue} object's current value. Listeners can veto the + * pending change by setting {@link #veto} to true. + * + * @since 1.0 + * + */ +public class ValueChangingEvent : ObservableEvent { + + /** + * + */ + private static final long serialVersionUID = 2305345286999701156L; + + static final Object TYPE = new Object(); + + /** + * Description of the change to the source observable value. Listeners must + * not change this field. + */ + public ValueDiff diff; + + /** + * Flag for vetoing this change. Default value is false, can + * be set to true by listeners to veto this change. + */ + public bool veto = false; + + /** + * Creates a new value changing event. + * + * @param source + * the source observable value + * @param diff + * the value change + */ + public this(IObservableValue source, ValueDiff diff) { + super(source); + this.diff = diff; + } + + /** + * @return the observable value from which this event originated + */ + public IObservableValue getObservableValue() { + return cast(IObservableValue) source; + } + + protected void dispatch(IObservablesListener listener) { + (cast(IValueChangingListener) listener).handleValueChanging(this); + } + + protected Object getListenerType() { + return TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueDiff.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueDiff.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.value.ValueDiff; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; + +/** + * @since 1.0 + * + */ +public abstract class ValueDiff { + /** + * Creates a value diff. + */ + public this() { + } + + /** + * @return the old value + */ + public abstract Object getOldValue(); + + /** + * @return the new value + */ + public abstract Object getNewValue(); + + public override bool opEquals(Object obj) { + if ( null !is cast(ValueDiff)obj ) { + ValueDiff val = cast(ValueDiff) obj; + + return Diffs.equals(val.getNewValue(), getNewValue()) + && Diffs.equals(val.getOldValue(), getOldValue()); + + } + return false; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + Object nv = getNewValue(); + Object ov = getOldValue(); + result = prime * result + ((nv is null) ? 0 : nv.hashCode()); + result = prime * result + ((ov is null) ? 0 : ov.hashCode()); + return result; + } + + /** + * @see java.lang.Object#toString() + */ + public String toString() { + StringBuffer buffer = new StringBuffer(); + buffer + .append(getClass().getName()) + .append("{oldValue [") //$NON-NLS-1$ + .append(getOldValue() !is null ? getOldValue().toString() : "null") //$NON-NLS-1$ + .append("], newValue [") //$NON-NLS-1$ + .append(getNewValue() !is null ? getNewValue().toString() : "null") //$NON-NLS-1$ + .append("]}"); //$NON-NLS-1$ + + return buffer.toString(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/WritableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/WritableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 158687 + * Brad Reynolds - bug 164653, 147515 + *******************************************************************************/ + +module org.eclipse.core.databinding.observable.value.WritableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; + +/** + * Mutable (writable) implementation of {@link IObservableValue} that will maintain a value and fire + * change events when the value changes. + *

+ * This class is thread safe. All state accessing methods must be invoked from + * the {@link Realm#isCurrent() current realm}. Methods for adding and removing + * listeners may be invoked from any thread. + *

+ * @since 1.0 + */ +public class WritableValue : AbstractObservableValue { + + private final Object valueType; + + /** + * Constructs a new instance with the default realm, a null + * value type, and a null value. + */ + public this() { + this(null, null); + } + + /** + * Constructs a new instance with the default realm. + * + * @param initialValue + * can be null + * @param valueType + * can be null + */ + public this(Object initialValue, Object valueType) { + this(Realm.getDefault(), initialValue, valueType); + } + + /** + * Constructs a new instance with the provided realm, a + * null value type, and a null initial value. + * + * @param realm + */ + public this(Realm realm) { + this(realm, null, null); + } + + /** + * Constructs a new instance. + * + * @param realm + * @param initialValue + * can be null + * @param valueType + * can be null + */ + public this(Realm realm, Object initialValue, Object valueType) { + super(realm); + this.valueType = valueType; + this.value = initialValue; + } + + private Object value = null; + + public Object doGetValue() { + return value; + } + + /** + * @param value + * The value to set. + */ + public void doSetValue(Object value) { + bool changed = false; + + if (this.value is null && value !is null) { + changed = true; + } else if (this.value !is null && !this.value.equals(value)) { + changed = true; + } + + if (changed) { + fireValueChange(Diffs.createValueDiff(this.value, this.value = value)); + } + } + + public Object getValueType() { + return valueType; + } + + /** + * @param elementType can be null + * @return new instance with the default realm and a value of null + */ + public static WritableValue withValueType(Object elementType) { + return new WritableValue(Realm.getDefault(), null, elementType); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides classes that can be used to observe changes in discrete values. +

+Package Specification

+

+This package provides classes that can be used to observe changes in discrete values.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,42 @@ + + + + + + + Package-level Javadoc + + +Provides classes for binding observable objects, for example UI widgets and model objects. +

+Package Specification

+

+This package provides classes that can be used to synchronize state between pairs of +observable objects with optional data type conversion and validation. +

+A DataBindingContext is used to manage a list of Bindings +with their validation results. +

+

+Concrete subclasses of Binding synchronize state between two observables, +called the target observable and the model observable. Usually, the binding will +first copy the current state of the model observable to the target observable and +from then on track changes on both sides, reacting to changes on one side by +performing the corresponding change on the other side. +

+

+For each binding, two UpdateValueStrategy or UpdateListStrategy objects (one +for each direction) is used to control how the binding should synchronize, and can be used to +specify data type converters and validators. +

+

+AggregateValidationStatus allows clients to aggregate the current validation +statuses of a list of bindings, typically obtained from a data binding context. +

+

+For advanced validation, conversion, or similar requirements that affect the way +state is copied from one side to the other, subclasses of UpdateValueStrategy +or UpdateListStrategy can be created. +

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/ILogger.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/ILogger.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * 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: + * Chris Gross (schtoo@schtoo.com) - initial API and implementation + * (bug 49497 [RCP] JFace dependency on org.eclipse.core.runtime enlarges standalone JFace applications) + *******************************************************************************/ + +module org.eclipse.core.databinding.util.ILogger; + +import java.lang.all; + +import org.eclipse.core.runtime.IStatus; + +/** + * A mechanism to log errors throughout JFace Data Binding. + *

+ * Clients may provide their own implementation to change how errors are logged + * from within JFace Data Binding. + *

+ * + * @see Policy#getLog() + * @see Policy#setLogcast(ILogger) + * @since 1.1 + */ +public interface ILogger { + + /** + * Logs the given status. + * + * @param status + * the status to log + */ + public void log(IStatus status); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/Policy.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/Policy.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2004, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Chris Gross (schtoo@schtoo.com) - support for ILogger added + * (bug 49497 [RCP] JFace dependency on org.eclipse.core.runtime enlarges standalone JFace applications) + * Brad Reynolds - bug 164653 + * Tom Schindl - bug 194587 + *******************************************************************************/ +module org.eclipse.core.databinding.util.Policy; + +import java.lang.all; + +import org.eclipse.core.runtime.IStatus; + +/** + * The Policy class handles settings for behaviour, debug flags and logging + * within JFace Data Binding. + * + * @since 1.1 + */ +public class Policy { + + /** + * Constant for the the default setting for debug options. + */ + public static final bool DEFAULT = false; + + /** + * The unique identifier of the JFace plug-in. + */ + public static final String JFACE_DATABINDING = "org.eclipse.core.databinding";//$NON-NLS-1$ + + private static ILogger log; + + /** + * Returns the dummy log to use if none has been set + */ + private static ILogger getDummyLog() { + return new class() ILogger { + public void log(IStatus status) { + System.err.println(status.getPlugin() + " - " + status.getCode() + " - " + status.getMessage()); //$NON-NLS-1$//$NON-NLS-2$ + if( status.getException() !is null ) { + status.getException().printStackTrace(System.err); + } + } + }; + } + + /** + * Sets the logger used by JFace Data Binding to log errors. + * + * @param logger + * the logger to use, or null to use the default + * logger + */ + public static synchronized void setLog(ILogger logger) { + log = logger; + } + + /** + * Returns the logger used by JFace Data Binding to log errors. + *

+ * The default logger prints the status to System.err. + *

+ * + * @return the logger + */ + public static synchronized ILogger getLog() { + if (log is null) { + log = getDummyLog(); + } + return log; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides general utilities for data binding. +

+Package Specification

+

+This package provides general utilities for data binding.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/IValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/IValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.databinding.validation.IValidator; + +import java.lang.all; + +import org.eclipse.core.runtime.IStatus; + +/** + * A validator. This validator is responsible for determining if a given value + * is valid. Validators can be used on target or model values. For example, a + * String2IntValidator would only accept source Strings that can successfully be + * converted to an integer value, and a PositiveIntegerValidator would only + * accept positive integers. + * + * @since 1.0 + * + */ +public interface IValidator { + + /** + * Determines if the given value is valid. + * + * @param value + * the value to validate + * @return a status object indicating whether the validation succeeded + * {@link IStatus#isOK()} or not. Never null. + */ + public IStatus validate(Object value); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/MultiValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/MultiValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,371 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 218269) + * Boris Bokowski - bug 218269 + ******************************************************************************/ + +module org.eclipse.core.databinding.validation.MultiValidator; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Arrays; + +import org.eclipse.core.databinding.ValidationStatusProvider; +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Observables; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiffVisitor; +import org.eclipse.core.databinding.observable.list.WritableList; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.WritableValue; +import org.eclipse.core.internal.databinding.observable.ValidatedObservableList; +import org.eclipse.core.internal.databinding.observable.ValidatedObservableMap; +import org.eclipse.core.internal.databinding.observable.ValidatedObservableSet; +import org.eclipse.core.internal.databinding.observable.ValidatedObservableValue; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; + +/** + * A validator for cross-constraints between observables. + * + *

+ * Some practical examples of cross-constraints: + *

    + *
  • A start date cannot be later than an end date + *
  • A list of percentages should add up to 100% + *
+ *

+ * Example: require two integer fields to contain either both even or both odd + * numbers. + * + *

+ * DataBindingContext dbc = new DataBindingContext();
+ * 
+ * IObservableValue target0 = SWTObservables.observeText(text0, SWT.Modify);
+ * IObservableValue target1 = SWTObservables.observeText(text1, SWT.Modify);
+ * 
+ * // Binding in two stages (from target to middle, then from middle to model)
+ * // simplifies the validation logic.  Using the middle observables saves
+ * // the trouble of converting the target values cast(Strings) to the model type
+ * // (integers) manually during validation.
+ * final IObservableValue middle0 = new WritableValue(null, Integer.TYPE);
+ * final IObservableValue middle1 = new WritableValue(null, Integer.TYPE);
+ * dbc.bind(target0, middle0, null, null);
+ * dbc.bind(target1, middle1, null, null);
+ * 
+ * // Create the multi-validator
+ * MultiValidator validator = new class() MultiValidator {
+ *  protected IStatus validate() {
+ *      // Calculate the validation status
+ *      Integer value0 = cast(Integer) middle0.getValue();
+ *      Integer value1 = cast(Integer) middle1.getValue();
+ *      if (Math.abs(value0.intValue()) % 2 !is Math.abs(value1.intValue()) % 2)
+ *          return ValidationStatus
+ *                  .error("Values must be both even or both odd");
+ *      return ValidationStatus.ok();
+ *  }
+ * };
+ * dbc.addValidationStatusProvider(validator);
+ * 
+ * // Bind the middle observables to the model observables. 
+ * IObservableValue model0 = new WritableValue(new Integer(2), Integer.TYPE);
+ * IObservableValue model1 = new WritableValue(new Integer(4), Integer.TYPE);
+ * dbc.bind(middle0, model0, null, null);
+ * dbc.bind(middle1, model1, null, null);
+ * 
+ * + *

+ * MultiValidator can also prevent invalid data from being copied to model. This + * is done by wrapping each target observable in a validated observable, and + * then binding the validated observable to the model. + * + *

+ * 
+ * ...
+ * 
+ * // Validated observables do not change value until the validator passes. 
+ * IObservableValue validated0 = validator.observeValidatedValue(middle0);
+ * IObservableValue validated1 = validator.observeValidatedValue(middle1);
+ * IObservableValue model0 = new WritableValue(new Integer(2), Integer.TYPE);
+ * IObservableValue model1 = new WritableValue(new Integer(4), Integer.TYPE);
+ * // Bind to the validated value, not the middle/target
+ * dbc.bind(validated0, model0, null, null);
+ * dbc.bind(validated1, model1, null, null);
+ * 
+ * + * Note: No guarantee is made as to the order of updates when multiple validated + * observables change value at once (i.e. multiple updates pending when the + * status becomes valid). Therefore the model may be in an invalid state after + * the first but before the last pending update. + * + * @since 1.1 + */ +public abstract class MultiValidator : ValidationStatusProvider { + private Realm realm; + private IObservableValue validationStatus; + private IObservableValue unmodifiableValidationStatus; + private WritableList targets; + private IObservableList unmodifiableTargets; + private IObservableList models; + + IListChangeListener targetsListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + event.diff.accept(new class() ListDiffVisitor { + public void handleAdd(int index, Object element) { + (cast(IObservable) element) + .addChangeListener(dependencyListener); + } + + public void handleRemove(int index, Object element) { + (cast(IObservable) element) + .removeChangeListener(dependencyListener); + } + }); + } + }; + + private IChangeListener dependencyListener = new class() IChangeListener { + public void handleChange(ChangeEvent event) { + revalidate(); + } + }; + + /** + * Constructs a MultiValidator on the default realm. + */ + public this() { + this(Realm.getDefault()); + } + + /** + * Constructs a MultiValidator on the given realm. + * + * @param realm + * the realm on which validation takes place. + */ + public this(Realm realm) { + Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$ + this.realm = realm; + + validationStatus = new WritableValue(realm, ValidationStatus.ok(), + IStatus.classinfo); + + targets = new WritableList(realm, new ArrayList(), IObservable.classinfo); + targets.addListChangeListener(targetsListener); + unmodifiableTargets = Observables.unmodifiableObservableList(targets); + + models = Observables.emptyObservableList(realm); + } + + private void checkObservable(IObservable target) { + Assert.isNotNull(target, "Target observable cannot be null"); //$NON-NLS-1$ + Assert + .isTrue(realm.equals(target.getRealm()), + "Target observable must be in the same realm as MultiValidator"); //$NON-NLS-1$ + } + + /** + * Returns an {@link IObservableValue} whose value is always the current + * validation status of this MultiValidator. The returned observable is in + * the same realm as this MultiValidator. + * + * @return an {@link IObservableValue} whose value is always the current + * validation status of this MultiValidator. + */ + public IObservableValue getValidationStatus() { + if (unmodifiableValidationStatus is null) { + revalidate(); + unmodifiableValidationStatus = Observables + .unmodifiableObservableValue(validationStatus); + } + return unmodifiableValidationStatus; + } + + private void revalidate() { + final IObservable[] dependencies = ObservableTracker.runAndMonitor( + new class() Runnable { + public void run() { + try { + IStatus status = validate(); + if (status is null) + status = ValidationStatus.ok(); + validationStatus.setValue(status); + } catch (RuntimeException e) { + // Usually an NPE as dependencies are + // init'ed + validationStatus.setValue(ValidationStatus.error(e + .getMessage(), e)); + } + } + }, null, null); + ObservableTracker.runAndIgnore(new class() Runnable { + public void run() { + targets.clear(); + targets.addAll(Arrays.asList(dependencies)); + } + }); + } + + /** + * Return the current validation status. + *

+ * Note: To ensure that the validation status is kept current, all + * dependencies used to calculate status should be accessed through + * {@link IObservable} instances. Each dependency observable must be in the + * same realm as the MultiValidator. + * + * @return the current validation status. + */ + protected abstract IStatus validate(); + + /** + * Returns a wrapper {@link IObservableValue} which stays in sync with the + * given target observable only when the validation status is valid. + * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or + * {@link IStatus#WARNING WARNING} severity are considered valid. + *

+ * The wrapper behaves as follows with respect to the validation status: + *

    + *
  • While valid, the wrapper stays in sync with its target observable. + *
  • While invalid, the wrapper's value is the target observable's last + * valid value. If the target changes value, a stale event is fired + * signaling that a change is pending. + *
  • When status changes from invalid to valid, the wrapper takes the + * value of the target observable, and synchronization resumes. + *
+ * + * @param target + * the target observable being wrapped. Must be in the same realm + * as the MultiValidator. + * @return an IObservableValue which stays in sync with the given target + * observable only with the validation status is valid. + */ + public IObservableValue observeValidatedValue(IObservableValue target) { + checkObservable(target); + return new ValidatedObservableValue(target, getValidationStatus()); + } + + /** + * Returns a wrapper {@link IObservableList} which stays in sync with the + * given target observable only when the validation status is valid. + * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or + * {@link IStatus#WARNING WARNING} severity are considered valid. + *

+ * The wrapper behaves as follows with respect to the validation status: + *

    + *
  • While valid, the wrapper stays in sync with its target observable. + *
  • While invalid, the wrapper's elements are the target observable's + * last valid elements. If the target changes elements, a stale event is + * fired signaling that a change is pending. + *
  • When status changes from invalid to valid, the wrapper takes the + * elements of the target observable, and synchronization resumes. + *
+ * + * @param target + * the target observable being wrapped. Must be in the same realm + * as the MultiValidator. + * @return an IObservableValue which stays in sync with the given target + * observable only with the validation status is valid. + */ + public IObservableList observeValidatedList(IObservableList target) { + checkObservable(target); + return new ValidatedObservableList(target, getValidationStatus()); + } + + /** + * Returns a wrapper {@link IObservableSet} which stays in sync with the + * given target observable only when the validation status is valid. + * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or + * {@link IStatus#WARNING WARNING} severity are considered valid. + *

+ * The wrapper behaves as follows with respect to the validation status: + *

    + *
  • While valid, the wrapper stays in sync with its target observable. + *
  • While invalid, the wrapper's elements are the target observable's + * last valid elements. If the target changes elements, a stale event is + * fired signaling that a change is pending. + *
  • When status changes from invalid to valid, the wrapper takes the + * elements of the target observable, and synchronization resumes. + *
+ * + * @param target + * the target observable being wrapped. Must be in the same realm + * as the MultiValidator. + * @return an IObservableValue which stays in sync with the given target + * observable only with the validation status is valid. + */ + public IObservableSet observeValidatedSet(IObservableSet target) { + checkObservable(target); + return new ValidatedObservableSet(target, getValidationStatus()); + } + + /** + * Returns a wrapper {@link IObservableMap} which stays in sync with the + * given target observable only when the validation status is valid. + * Statuses of {@link IStatus#OK OK}, {@link IStatus#INFO INFO} or + * {@link IStatus#WARNING WARNING} severity are considered valid. + *

+ * The wrapper behaves as follows with respect to the validation status: + *

    + *
  • While valid, the wrapper stays in sync with its target observable. + *
  • While invalid, the wrapper's entries are the target observable's + * last valid entries. If the target changes entries, a stale event is fired + * signaling that a change is pending. + *
  • When status changes from invalid to valid, the wrapper takes the + * entries of the target observable, and synchronization resumes. + *
+ * + * @param target + * the target observable being wrapped. Must be in the same realm + * as the MultiValidator. + * @return an IObservableValue which stays in sync with the given target + * observable only with the validation status is valid. + */ + public IObservableMap observeValidatedMap(IObservableMap target) { + checkObservable(target); + return new ValidatedObservableMap(target, getValidationStatus()); + } + + public IObservableList getTargets() { + return unmodifiableTargets; + } + + public IObservableList getModels() { + return models; + } + + public void dispose() { + targets.clear(); // Remove listeners from dependencies + + unmodifiableValidationStatus.dispose(); + validationStatus.dispose(); + unmodifiableTargets.dispose(); + targets.dispose(); + models.dispose(); + + realm = null; + validationStatus = null; + unmodifiableValidationStatus = null; + targets = null; + unmodifiableTargets = null; + models = null; + + super.dispose(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/ValidationStatus.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/ValidationStatus.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164134 + *******************************************************************************/ +module org.eclipse.core.databinding.validation.ValidationStatus; + +import java.lang.all; + +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Convenience class for creating status objects. + * + * @since 3.3 + * + */ +public class ValidationStatus : Status { + + /** + * Creates a new validation status with the given severity, message, and + * exception. + * + * @param severity + * @param message + * @param exception + */ + private this(int severity, String message, Throwable exception) { + super(severity, Policy.JFACE_DATABINDING, IStatus.OK, message, exception); + } + + /** + * Creates a new validation status with the given severity and message. + * + * @param severity + * @param message + */ + private this(int severity, String message) { + super(severity, Policy.JFACE_DATABINDING,IStatus.OK, message, null); + } + + /** + * Creates a new validation error status with the given message. + * + * @param message + * @return a new error status with the given message + */ + public static IStatus error(String message) { + return new ValidationStatus(IStatus.ERROR, message); + } + + /** + * Creates a new validation cancel status with the given message. + * + * @param message + * @return a new cancel status with the given message + */ + public static IStatus cancel(String message) { + return new ValidationStatus(IStatus.CANCEL, message); + } + + /** + * Creates a new validation error status with the given message and + * exception. + * + * @param message + * @param exception + * @return a new error status with the given message and exception + */ + public static IStatus error(String message, Throwable exception) { + return new ValidationStatus(IStatus.ERROR, message, exception); + } + + /** + * Creates a new validation warning status with the given message. + * + * @param message + * @return a new warning status with the given message + */ + public static IStatus warning(String message) { + return new ValidationStatus(IStatus.WARNING, message); + } + + /** + * Creates a new validation info status with the given message. + * + * @param message + * @return a new info status with the given message + */ + public static IStatus info(String message) { + return new ValidationStatus(IStatus.INFO, message); + } + + /** + * Returns an OK status. + * + * @return an ok status + */ + public static IStatus ok() { + return Status.OK_STATUS; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + final int prime = 31; + int result = 1; + + String message = getMessage(); + int severity = getSeverity(); + Throwable throwable = getException(); + + result = prime * result + ((message is null) ? 0 : message.hashCode()); + result = prime * result + severity; + result = prime * result + + ((throwable is null) ? 0 : throwable.hashCode()); + return result; + } + + /** + * Equality is based upon instance equality rather than identity. + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public override bool opEquals(Object obj) { + if (this is obj) + return true; + if (obj is null) + return false; + if (getClass() !is obj.getClass()) + return false; + final ValidationStatus other = cast(ValidationStatus) obj; + + if (getSeverity() !is other.getSeverity()) + return false; + if (getMessage() is null) { + if (other.getMessage() !is null) + return false; + } else if (!getMessage().equals(other.getMessage())) + return false; + if (getException() is null) { + if (other.getException() !is null) + return false; + } else if (!getException().equals(other.getException())) + return false; + return true; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides the core APIs for validation. +

+Package Specification

+

+This package provides the core APIs for validation.

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Activator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Activator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2007 Tom Schindl and others. + * 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: + * Tom Schindl - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.Activator; + +import java.lang.all; + +import java.util.ArrayList; + +import org.eclipse.core.databinding.util.ILogger; +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.osgi.framework.log.FrameworkLog; +import org.eclipse.osgi.framework.log.FrameworkLogEntry; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.util.tracker.ServiceTracker; + +/** + * @since 3.3 + * + */ +public class Activator : BundleActivator { + /** + * The plug-in ID + */ + public static final String PLUGIN_ID = "org.eclipse.core.databinding"; //$NON-NLS-1$ + + private /+volatile+/ static ServiceTracker _frameworkLogTracker; + + /** + * The constructor + */ + public this() { + } + + public void start(BundleContext context) { + _frameworkLogTracker = new ServiceTracker(context, FrameworkLog.classinfo.getName(), null); + _frameworkLogTracker.open(); + + Policy.setLog(new class() ILogger { + + public void log(IStatus status) { + ServiceTracker frameworkLogTracker = _frameworkLogTracker; + FrameworkLog log = frameworkLogTracker is null ? null : cast(FrameworkLog) frameworkLogTracker.getService(); + if (log !is null) { + log.log(createLogEntry(status)); + } else { + // fall back to System.err + System.err.println(status.getPlugin() + " - " + status.getCode() + " - " + status.getMessage()); //$NON-NLS-1$//$NON-NLS-2$ + if( status.getException() !is null ) { + status.getException().printStackTrace(System.err); + } + } + } + + }); + } + + // Code copied from PlatformLogWriter.getLog(). Why is logging an IStatus so + // hard? + FrameworkLogEntry createLogEntry(IStatus status) { + Throwable t = status.getException(); + ArrayList childlist = new ArrayList(); + + int stackCode = null !is cast(CoreException )t ? 1 : 0; + // ensure a substatus inside a CoreException is properly logged + if (stackCode is 1) { + IStatus coreStatus = (cast(CoreException) t).getStatus(); + if (coreStatus !is null) { + childlist.add(createLogEntry(coreStatus)); + } + } + + if (status.isMultiStatus()) { + IStatus[] children = status.getChildren(); + for (int i = 0; i < children.length; i++) { + childlist.add(createLogEntry(children[i])); + } + } + + FrameworkLogEntry[] children = cast(FrameworkLogEntry[]) (childlist.size() is 0 ? null : childlist.toArray(new FrameworkLogEntry[childlist.size()])); + + return new FrameworkLogEntry(status.getPlugin(), status.getSeverity(), status.getCode(), status.getMessage(), stackCode, t, children); + } + + + public void stop(BundleContext context) { + if (_frameworkLogTracker !is null) { + _frameworkLogTracker.close(); + _frameworkLogTracker = null; + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingMessages.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingMessages.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Tom Schindl - bugfix for 217940 + *******************************************************************************/ +module org.eclipse.core.internal.databinding.BindingMessages; + +import java.lang.all; + +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import com.ibm.icu.text.MessageFormat; + +/** + * @since 1.0 + * + */ +public class BindingMessages { + + /** + * The Binding resource bundle; eagerly initialized. + */ + private static final ResourceBundle bundle = ResourceBundle + .getBundle("org.eclipse.core.internal.databinding.messages"); //$NON-NLS-1$ + + /** + * Key to be used for an index out of range message. + */ + public static final String INDEX_OUT_OF_RANGE = "IndexOutOfRange"; //$NON-NLS-1$ + + /** + * Key to be used for a "Multiple Problems." message. + */ + public static final String MULTIPLE_PROBLEMS = "MultipleProblems"; //$NON-NLS-1$ + + /** + * Key to be used for a "ValueBinding_ErrorWhileSettingValue" message + */ + public static final String VALUEBINDING_ERROR_WHILE_SETTING_VALUE = "ValueBinding_ErrorWhileSettingValue"; //$NON-NLS-1$ + + /** + * Key to be used for a "DateFormat_DateTime" message + */ + public static final String DATE_FORMAT_DATE_TIME = "DateFormat_DateTime"; //$NON-NLS-1$ + + /** + * Key to be used for a "DateFormat_Time" message + */ + public static final String DATEFORMAT_TIME = "DateFormat_Time"; //$NON-NLS-1$ + + /** + * Key to be used for a "ValueDelimiter" message + */ + public static final String VALUE_DELIMITER = "ValueDelimiter"; //$NON-NLS-1$ + + /** + * Key to be used for a "TrueStringValues" message + */ + public static final String TRUE_STRING_VALUES = "TrueStringValues"; //$NON-NLS-1$ + + /** + * Key to be used for a "FalseStringValues" message + */ + public static final String FALSE_STRING_VALUES = "FalseStringValues"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberOutOfRangeError" message + */ + public static final String VALIDATE_NUMBER_OUT_OF_RANGE_ERROR = "Validate_NumberOutOfRangeError"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberParseError" message + */ + public static final String VALIDATE_NUMBER_PARSE_ERROR = "Validate_NumberParseError"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_ConversionToPrimitive" message + */ + public static final String VALIDATE_CONVERSION_TO_PRIMITIVE = "Validate_ConversionToPrimitive"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_ConversionFromClassToPrimitive" message + */ + public static final String VALIDATE_CONVERSION_FROM_CLASS_TO_PRIMITIVE = "Validate_ConversionFromClassToPrimitive"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NoChangeAllowedHelp" message + */ + public static final String VALIDATE_NO_CHANGE_ALLOWED_HELP = "Validate_NoChangeAllowedHelp"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_CharacterHelp" message + */ + public static final String VALIDATE_CHARACTER_HELP = "Validate_CharacterHelp"; //$NON-NLS-1$ + + /** + * Key to be used for a "Examples" message + */ + public static final String EXAMPLES = "Examples"; //$NON-NLS-1$ + + /** + * Key to be used for a "Validate_NumberParseErrorNoCharacter" message + */ + public static final String VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER = "Validate_NumberParseErrorNoCharacter"; //$NON-NLS-1$ + + /** + * Returns the resource object with the given key in the resource bundle for + * JFace Data Binding. If there isn't any value under the given key, the key + * is returned. + * + * @param key + * the resource name + * @return the string + */ + public static String getString(String key) { + try { + return bundle.getString(key); + } catch (MissingResourceException e) { + return key; + } + } + + /** + * Returns a formatted string with the given key in the resource bundle for + * JFace Data Binding. + * + * @param key + * @param arguments + * @return formatted string, the key if the key is invalid + */ + public static String formatString(String key, Object[] arguments) { + try { + return MessageFormat.format(bundle.getString(key), arguments); + } catch (MissingResourceException e) { + return key; + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingStatus.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingStatus.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.BindingStatus; + +import java.lang.all; + +import java.util.Arrays; + +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; + +/** + * A MultiStatus implementation that copies that state of the + * added status to this instance if it is >= the current severity. + * + * @since 1.0 + */ +public class BindingStatus : MultiStatus { + /** + * Constructs a new instance. + * + * @param pluginId + * @param code + * @param message + * @param exception + */ + public this(String pluginId, int code, String message, + Throwable exception) { + super(pluginId, code, message, exception); + } + + /** + * Adds the status to the multi status. The details of the status will be + * copied to the multi status if the severity is >= the current severity. + * + * @see org.eclipse.core.runtime.MultiStatus#add(org.eclipse.core.runtime.IStatus) + */ + public void add(IStatus status) { + if (status.getSeverity() >= getSeverity()) { + setMessage((status.getMessage() !is null) ? status.getMessage() : ""); //$NON-NLS-1$ + setException(status.getException()); + setPlugin(status.getPlugin()); + setCode(status.getCode()); + } + + super.add(status); + } + + /** + * Instance initialized with the following values: + *
    + *
  • plugin = Policy.JFACE_DATABINDING
  • + *
  • severity = 0
  • + *
  • code = 0
  • + *
  • message = ""
  • + *
  • exception = null
  • + *
+ * + * @return status + */ + public static BindingStatus ok() { + return new BindingStatus(Policy.JFACE_DATABINDING, 0, "", null); //$NON-NLS-1$ + } + + private static int hashCode(Object[] array) { + final int prime = 31; + if (array is null) + return 0; + int result = 1; + for (int index = 0; index < array.length; index++) { + result = prime * result + + (array[index] is null ? 0 : array[index].hashCode()); + } + return result; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + BindingStatus.hashCode(getChildren()); + return result; + } + + public override bool opEquals(Object obj) { + if (this is obj) + return true; + if (obj is null) + return false; + if (getClass() !is obj.getClass()) + return false; + final BindingStatus other = cast(BindingStatus) obj; + if (!Arrays.equals(getChildren(), other.getChildren())) + return false; + return true; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ClassLookupSupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ClassLookupSupport.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.ClassLookupSupport; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * @since 1.0 + * + */ +public class ClassLookupSupport { + + /* + * code copied from AdapterManager.java + */ + private static HashMap classSearchOrderLookup; + + /** + * For a given class or interface, return an array containing the given type and all its direct and indirect supertypes. + * @param type + * @return an array containing the given type and all its direct and indirect supertypes + */ + public static ClassInfo[] getTypeHierarchyFlattened(ClassInfo type) { + List classes = null; + //cache reference to lookup to protect against concurrent flush + HashMap lookup = classSearchOrderLookup; + if (lookup !is null) + classes = cast(List) lookup.get(type); + // compute class order only if it hasn't been cached before + if (classes is null) { + classes = new ArrayList(); + computeClassOrder(type, classes); + if (lookup is null) + classSearchOrderLookup = lookup = new HashMap(); + lookup.put(type, classes); + } + return cast(ClassInfo[]) classes.toArray(new ClassInfo[classes.size()]); + } + + /** + * Builds and returns a table of adapters for the given adaptable type. + * The table is keyed by adapter class name. The + * value is the sole factory that defines that adapter. Note that + * if multiple adapters technically define the same property, only the + * first found in the search order is considered. + * + * Note that it is important to maintain a consistent class and interface + * lookup order. See the class comment for more details. + */ + private static void computeClassOrder(ClassInfo adaptable, Collection classes) { + ClassInfo clazz = adaptable; + Set seen = new HashSet(4); + while (clazz !is null) { + classes.add(clazz); + computeInterfaceOrder(clazz.getInterfaces(), classes, seen); + clazz = clazz.isInterface() ? Object.classinfo : clazz.getSuperclass(); + } + } + + private static void computeInterfaceOrder(ClassInfo[] interfaces, Collection classes, Set seen) { + List newInterfaces = new ArrayList(interfaces.length); + for (int i = 0; i < interfaces.length; i++) { + ClassInfo interfac = interfaces[i]; + if (seen.add(interfac)) { + //note we cannot recurse here without changing the resulting interface order + classes.add(interfac); + newInterfaces.add(interfac); + } + } + for (Iterator it = newInterfaces.iterator(); it.hasNext();) + computeInterfaceOrder((cast(ClassInfo) it.next()).getInterfaces(), classes, seen); + } + + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityWrapper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityWrapper.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Daniel Kruegler - bug 137435 + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.IdentityWrapper; + +import java.lang.all; + +/** + * Used for wrapping objects that define their own implementations of equals() + * and hashCode() when putting them in sets or hashmaps to ensure identity + * comparison. + * + * @since 1.0 + * + */ +public class IdentityWrapper { + final Object o; + + /** + * @param o + */ + public this(Object o) { + this.o = o; + } + + /** + * @return the unwrapped object + */ + public Object unwrap() { + return o; + } + + public override bool opEquals(Object obj) { + if (obj is null || obj.getClass() !is IdentityWrapper.classinfo) { + return false; + } + return o is (cast(IdentityWrapper) obj).o; + } + + public int hashCode() { + return System.identityHashCode(o); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Pair.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Pair.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.internal.databinding.Pair; + +import java.lang.all; + +/** + * Class Pair. Represents a mathematical pair of objects (a, b). + * @since 1.0 + */ +public class Pair { + + /** + * a in the pair (a, b) + */ + public final Object a; + + /** + * b in the pair (a, b) + */ + public final Object b; + + /** + * Construct a Pair(a, b) + * + * @param a a in the pair (a, b) + * @param b b in the pair (a, b) + */ + public this(Object a, Object b) { + this.a = a; + this.b = b; + } + + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((a is null) ? 0 : a.hashCode()); + result = prime * result + ((b is null) ? 0 : b.hashCode()); + return result; + } + + public override bool opEquals(Object obj) { + if (this is obj) + return true; + if (obj is null) + return false; + if (getClass() !is obj.getClass()) + return false; + Pair other = cast(Pair) obj; + if (a is null) { + if (other.a !is null) + return false; + } else if (!a.equals(other.a)) + return false; + if (b is null) { + if (other.b !is null) + return false; + } else if (!b.equals(other.b)) + return false; + return true; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Queue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Queue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.internal.databinding.Queue; + +import java.lang.all; + +/** + * Created to avoid a dependency on java.util.LinkedList, see bug 205224. + * + * @since 1.1 + * + */ +public class Queue { + + static class Entry { + Object object; + + this(Object o) { + this.object = o; + } + + Entry next; + } + + Entry first; + Entry last; + + /** + * Adds the given object to the end of the queue. + * + * @param o + */ + public void enqueue(Object o) { + Entry oldLast = last; + last = new Entry(o); + if (oldLast !is null) { + oldLast.next = last; + } else { + first = last; + } + } + + /** + * Returns the first object in the queue. The queue must not be empty. + * + * @return the first object + */ + public Object dequeue() { + Entry oldFirst = first; + if (oldFirst is null) { + throw new IllegalStateException(); + } + first = oldFirst.next; + if (first is null) { + last = null; + } + oldFirst.next = null; + return oldFirst.object; + } + + /** + * Returns true if the list is empty. + * + * @return true if the list is empty + */ + public bool isEmpty() { + return first is null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/RandomAccessListIterator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/RandomAccessListIterator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2006 The Pampered Chef and others. + * 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: + * The Pampered Chef - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.RandomAccessListIterator; + +import java.lang.all; + +import java.util.List; +import java.util.ListIterator; + +/** + * Class RandomAccessListIterator. A ListIterator implementation that also + * provides access to individual elements based on the element's index. + * + * @since 3.3 + */ +public class RandomAccessListIterator : ListIterator { + private ListIterator delegate_ = null; + + /** + * @param iterator + */ + public this(ListIterator iterator) { + this.delegate_ = iterator; + } + + /** + * @param list + */ + public this(List list) { + if (list is null) { + throw new IllegalArgumentException("list is null"); //$NON-NLS-1$ + } + this.delegate_ = list.listIterator(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#add(java.lang.Object) + */ + public void add(Object arg0) { + delegate_.add(arg0); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#hasNext() + */ + public bool hasNext() { + return delegate_.hasNext(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#hasPrevious() + */ + public bool hasPrevious() { + return delegate_.hasPrevious(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#next() + */ + public Object next() { + return delegate_.next(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#nextIndex() + */ + public int nextIndex() { + return delegate_.nextIndex(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#previous() + */ + public Object previous() { + return delegate_.previous(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#previousIndex() + */ + public int previousIndex() { + return delegate_.previousIndex(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#remove() + */ + public void remove() { + delegate_.remove(); + } + + /* (non-Javadoc) + * @see java.util.ListIterator#set(java.lang.Object) + */ + public void set(Object arg0) { + delegate_.set(arg0); + } + + /** + * Return the element at the specified position by moving the iterator + * forward or backward in the list until it reaches the correct element. + * The iterator's position after returning the element will be one after + * the element returned. + * + * @param index The (0-based) index of the element to return. + * @return the Object at index + */ + public Object get(int index) { + if (delegate_.nextIndex() is 0 && !delegate_.hasNext()) { + throw new IndexOutOfBoundsException("Request for element from empty list"); //$NON-NLS-1$ + } + if (index < 0) { + throw new IndexOutOfBoundsException("Request for negative element index"); //$NON-NLS-1$ + } + + while (nextIndex() < index && hasNext()) { + next(); + } + while (previousIndex() > index-1) { + previous(); + } + if (!hasNext()) { + throw new IndexOutOfBoundsException("Request for element past end of list"); //$NON-NLS-1$ + } + return next(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Util.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Util.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.Util; + +import java.lang.all; + +/** + * @since 3.3 + * + */ +public class Util { + + /** + * Checks whether the two objects are null -- allowing for + * null. + * + * @param left + * The left object to compare; may be null. + * @param right + * The right object to compare; may be null. + * @return true if the two objects are equivalent; + * false otherwise. + */ + public static final bool equals(Object left, Object right) { + return left is null ? right is null : ((right !is null) && left + .opEquals(right)); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.ValidationStatusMap; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.Binding; +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.WritableList; +import org.eclipse.core.databinding.observable.map.IMapChangeListener; +import org.eclipse.core.databinding.observable.map.MapDiff; +import org.eclipse.core.databinding.observable.map.ObservableMap; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 1.0 + * + */ +public class ValidationStatusMap : ObservableMap { + + private bool isDirty = true; + + private final WritableList bindings; + + private List dependencies = new ArrayList(); + + private IChangeListener markDirtyChangeListener = new class() IChangeListener { + public void handleChange(ChangeEvent event) { + markDirty(); + } + }; + + /** + * @param realm + * @param bindings + */ + public this(Realm realm, WritableList bindings) { + super(realm, new HashMap()); + this.bindings = bindings; + bindings.addChangeListener(markDirtyChangeListener); + } + + protected void getterCalled() { + recompute(); + super.getterCalled(); + } + + private void markDirty() { + // since we are dirty, we don't need to listen anymore + removeElementChangeListener(); + final Map oldMap = wrappedMap; + // lazy computation of diff + MapDiff mapDiff = new class() MapDiff { + private MapDiff cachedDiff = null; + + private void ensureCached() { + if (cachedDiff is null) { + recompute(); + cachedDiff = Diffs.computeMapDiff(oldMap, wrappedMap); + } + } + + public Set getAddedKeys() { + ensureCached(); + return cachedDiff.getAddedKeys(); + } + + public Set getChangedKeys() { + ensureCached(); + return cachedDiff.getChangedKeys(); + } + + public Object getNewValue(Object key) { + ensureCached(); + return cachedDiff.getNewValue(key); + } + + public Object getOldValue(Object key) { + ensureCached(); + return cachedDiff.getOldValue(key); + } + + public Set getRemovedKeys() { + ensureCached(); + return cachedDiff.getRemovedKeys(); + } + }; + wrappedMap = new HashMap(); + isDirty = true; + fireMapChange(mapDiff); + } + + private void recompute() { + if (isDirty) { + Map newContents = new HashMap(); + for (Iterator it = bindings.iterator(); it.hasNext();) { + Binding binding = cast(Binding) it.next(); + IObservableValue validationError = binding + .getValidationStatus(); + dependencies.add(validationError); + validationError.addChangeListener(markDirtyChangeListener); + IStatus validationStatusValue = cast(IStatus) validationError + .getValue(); + newContents.put(binding, validationStatusValue); + } + wrappedMap.putAll(newContents); + isDirty = false; + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.list.ObservableList#dispose() + */ + public void dispose() { + bindings.removeChangeListener(markDirtyChangeListener); + removeElementChangeListener(); + super.dispose(); + } + + private void removeElementChangeListener() { + for (Iterator it = dependencies.iterator(); it.hasNext();) { + IObservableValue observableValue = cast(IObservableValue) it.next(); + observableValue.removeChangeListener(markDirtyChangeListener); + } + } + + public synchronized void addChangeListener(IChangeListener listener) { + // this ensures that the next change will be seen by the new listener. + recompute(); + super.addChangeListener(listener); + } + + public synchronized void addMapChangeListener(IMapChangeListener listener) { + // this ensures that the next change will be seen by the new listener. + recompute(); + super.addMapChangeListener(listener); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/CharacterToStringConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/CharacterToStringConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2007 Matt Carter and others. + * 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: + * Matt Carter - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.CharacterToStringConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.Converter; + +/** + * Converts a character to a string. + */ +public class CharacterToStringConverter : Converter { + private final bool primitive; + + /** + * @param primitive + */ + private this(bool primitive) { + super(primitive ? Character.TYPE : Character.classinfo, String.classinfo); + this.primitive = primitive; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + */ + public Object convert(Object fromObject) { + // Null is allowed when the type is not primitive. + if (fromObject is null) { + if (primitive) + throw new IllegalArgumentException( + "'fromObject' is null. Cannot convert to primitive char."); //$NON-NLS-1$ + return ""; //$NON-NLS-1$ + } + + if (!( null !is cast(Character)fromObject )) { + throw new IllegalArgumentException( + "'fromObject' is not of type [Character]."); //$NON-NLS-1$ + } + + return String.valueOf((cast(Character) fromObject).charValue()); + } + + /** + * @param primitive + * @return converter + */ + public static CharacterToStringConverter fromCharacter(bool primitive) { + return new CharacterToStringConverter(primitive); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,131 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + * Tom Schindl - bugfix for 217940 + */ +module org.eclipse.core.internal.databinding.conversion.DateConversionSupport; + +import java.lang.all; + +import java.text.ParsePosition; +import java.util.Date; + +import org.eclipse.core.internal.databinding.BindingMessages; + +import com.ibm.icu.text.DateFormat; +import com.ibm.icu.text.SimpleDateFormat; + +/** + * Base support for date/string conversion handling according to the default + * locale or in plain long milliseconds. + *

+ * NOTE: parse(format(date)) will generally *not* be equal to date, since the + * string representation may not cover the sub-second range, time-only string + * representations will be counted from the beginning of the era, etc. + *

+ */ +public abstract class DateConversionSupport { + private final static int DATE_FORMAT=DateFormat.SHORT; + private final static int DEFAULT_FORMATTER_INDEX=0; + + private final static int NUM_VIRTUAL_FORMATTERS=1; + + /** + * Alternative formatters for date, time and date/time. + * Raw milliseconds are covered as a special case. + */ + // TODO: These could be shared, but would have to be synchronized. + private DateFormat[] formatters = { + new SimpleDateFormat(BindingMessages.getStringcast(BindingMessages.DATE_FORMAT_DATE_TIME)), + new SimpleDateFormat(BindingMessages.getStringcast(BindingMessages.DATEFORMAT_TIME)), + DateFormat.getDateTimeInstance(DATE_FORMAT, DateFormat.SHORT), + DateFormat.getDateInstancecast(DATE_FORMAT), + DateFormat.getTimeInstancecast(DateFormat.SHORT), + DateFormat.getDateTimeInstance(DATE_FORMAT,DateFormat.MEDIUM), + DateFormat.getTimeInstancecast(DateFormat.MEDIUM) + }; + + /** + * Tries all available formatters to parse the given string according to the + * default locale or as a raw millisecond value and returns the result of the + * first successful run. + * + * @param str A string specifying a date according to the default locale or in raw milliseconds + * @return The parsed date, or null, if no available formatter could interpret the input string + */ + protected Date parse(String str) { + for (int formatterIdx = 0; formatterIdx < formatters.length; formatterIdx++) { + Date parsed=parse(str,formatterIdx); + if(parsed !is null) { + return parsed; + } + } + return null; + } + + protected Date parse(String str,int formatterIdx) { + if(formatterIdx>=0) { + ParsePosition pos=new ParsePosition(0); + if (str is null) { + return null; + } + Date date=formatters[formatterIdx].parse(str,pos); + if(pos.getErrorIndex() !is -1||pos.getIndex() !is str.length()) { + return null; + } + return date; + } + try { + long millisecs=Long.parseLong(str); + return new Date(millisecs); + } + catch(NumberFormatException exc) { + } + return null; + } + + /** + * Formats the given date with the default formatter according to the default locale. + * @param date a date + * @return a string representation of the given date according to the default locale + */ + protected String format(Date date) { + return format(date,DEFAULT_FORMATTER_INDEX); + } + + protected String format(Date date,int formatterIdx) { + if(formatterIdx>=0) { + return formatters[formatterIdx].format(date); + } + return String.valueOf(date.getTime()); + } + + protected int numFormatters() { + return formatters.length+NUM_VIRTUAL_FORMATTERS; + } + + /** + * Returns the date format for the provided index. + *

+ * This is for testing purposes only and should not be a part of the API if + * this class was to be exposed. + *

+ * + * @param index + * @return date format + */ + protected DateFormat getDateFormat(int index) { + if (index < 0 || index >= formatters.length) { + throw new IllegalArgumentException("'index' [" + index + "] is out of bounds."); //$NON-NLS-1$//$NON-NLS-2$ + } + + return formatters[index]; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,41 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + */ +module org.eclipse.core.internal.databinding.conversion.DateToStringConverter; + +import java.lang.all; + +import java.util.Date; + +import org.eclipse.core.databinding.conversion.IConverter; + + +/** + * Converts a Java.util.Date to a String using the current locale. Null date + * values are converted to an empty string. + * + * @since 1.0 + */ +public class DateToStringConverter : DateConversionSupport , IConverter { + public Object convert(Object source) { + if (source !is null) + return format(cast(Date)source); + return ""; //$NON-NLS-1$ + } + + public Object getFromType() { + return Date.classinfo; + } + + public Object getToType() { + return String.classinfo; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IdentityConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IdentityConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,112 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com and others. + * + * 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: + * db4objects - Initial API and implementation + * Matt Carter - Character support completed (bug 197679) + */ +module org.eclipse.core.internal.databinding.conversion.IdentityConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.BindingException; +import org.eclipse.core.databinding.conversion.IConverter; + +/** + * TheIdentityConverter. Returns the source value (the identity function). + */ +public class IdentityConverter : IConverter { + + private ClassInfo fromType; + + private ClassInfo toType; + + /** + * @param type + */ + public this(ClassInfo type) { + this.fromType = type; + this.toType = type; + } + + /** + * @param fromType + * @param toType + */ + public this(ClassInfo fromType, ClassInfo toType) { + this.fromType = fromType; + this.toType = toType; + } + + private ClassInfo[][] primitiveMap = new ClassInfo[][] [ + [ Integer.TYPE, Integer.classinfo ], [ Short.TYPE, Short.classinfo ], + [ Long.TYPE, Long.classinfo ], [ Double.TYPE, Double.classinfo ], + [ Byte.TYPE, Byte.classinfo ], [ Float.TYPE, Float.classinfo ], + [ Boolean.TYPE, Boolean.classinfo ], + [ Character.TYPE, Character.classinfo ] ]; + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object) + */ + public Object convert(Object source) { + if (toType.isPrimitive()) { + if (source is null) { + throw new BindingException("Cannot convert null to a primitive"); //$NON-NLS-1$ + } + } + if (source !is null) { + ClassInfo sourceClass = source.getClass(); + if (toType.isPrimitive() || sourceClass.isPrimitive()) { + if (sourceClass.equals(toType) + || isPrimitiveTypeMatchedWithBoxed(sourceClass, toType)) { + return source; + } + throw new BindingException( + "Boxed and unboxed types do not match"); //$NON-NLS-1$ + } + if (!toType.isAssignableFrom(sourceClass)) { + throw new BindingException(sourceClass.getName() + + " is not assignable to " + toType.getName()); //$NON-NLS-1$ + } + } + return source; + } + + /** + * (Non-API) isPrimitiveTypeMatchedWithBoxed. + * + * @param sourceClass + * @param toClass + * @return true if sourceClass and toType are matched primitive/boxed types + */ + public bool isPrimitiveTypeMatchedWithBoxed(ClassInfo sourceClass, + ClassInfo toClass) { + for (int i = 0; i < primitiveMap.length; i++) { + if (toClass.equals(primitiveMap[i][0]) + && sourceClass.equals(primitiveMap[i][1])) { + return true; + } + if (sourceClass.equals(primitiveMap[i][0]) + && toClass.equals(primitiveMap[i][1])) { + return true; + } + } + return false; + } + + public Object getFromType() { + return fromType; + } + + public Object getToType() { + return toType; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IntegerToStringConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IntegerToStringConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.IntegerToStringConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.Converter; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts a value that is an integer, non decimal, to a String using a + * NumberFormat. + *

+ * This class is a temporary as this ability exists in NumberToStringConverter + * except that short and byte are missing. + *

+ * + * @since 1.0 + */ +public class IntegerToStringConverter : Converter { + private final bool primitive; + private final NumberFormat numberFormat; + private final ClassInfo boxedType; + + /** + * @param numberFormat + * @param fromType + * @param boxedType + */ + private this(NumberFormat numberFormat, ClassInfo fromType, + ClassInfo boxedType) { + super(fromType, String.classinfo); + this.primitive = fromType.isPrimitive(); + this.numberFormat = numberFormat; + this.boxedType = boxedType; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + */ + public Object convert(Object fromObject) { + // Null is allowed when the type is not primitve. + if (fromObject is null && !primitive) { + return ""; //$NON-NLS-1$ + } + + if (!boxedType.isInstance(fromObject)) { + throw new IllegalArgumentException( + "'fromObject' is not of type [" + boxedType + "]."); //$NON-NLS-1$//$NON-NLS-2$ + } + + return numberFormat.format((cast(Number) fromObject).longValue()); + } + + /** + * @param primitive + * @return converter + */ + public static IntegerToStringConverter fromShort(bool primitive) { + return fromShort(NumberFormat.getIntegerInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return converter + */ + public static IntegerToStringConverter fromShort(NumberFormat numberFormat, + bool primitive) { + return new IntegerToStringConverter(numberFormat, + primitive ? Short.TYPE : Short.classinfo, Short.classinfo); + } + + /** + * @param primitive + * @return converter + */ + public static IntegerToStringConverter fromByte(bool primitive) { + return fromByte(NumberFormat.getIntegerInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return converter + */ + public static IntegerToStringConverter fromByte(NumberFormat numberFormat, + bool primitive) { + return new IntegerToStringConverter(numberFormat, primitive ? Byte.TYPE + : Byte.classinfo, Byte.classinfo); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigDecimalConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigDecimalConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToBigDecimalConverter; + +import java.lang.all; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a BigDecimal. + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public class NumberToBigDecimalConverter : NumberToNumberConverter { + /** + * @param numberFormat + * @param fromType + */ + public this(NumberFormat numberFormat, ClassInfo fromType) { + super(numberFormat, fromType, BigDecimal.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + if ( null !is cast(BigInteger)number ) { + return new BigDecimal(cast(BigInteger) number); + } + + return new BigDecimal(number.doubleValue()); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigIntegerConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigIntegerConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter; + +import java.lang.all; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a BigInteger. + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public class NumberToBigIntegerConverter : NumberToNumberConverter { + /** + * @param numberFormat + * @param fromType + */ + public this(NumberFormat numberFormat, ClassInfo fromType) { + super(numberFormat, fromType, BigInteger.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + return toBigDecimal(number).toBigInteger(); + } + + private static BigDecimal toBigDecimal(Number number) { + if ( null !is cast(BigDecimal)number ) { + return cast(BigDecimal) number; + } + + return new BigDecimal(number.doubleValue()); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToByteConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToByteConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToByteConverter; + +import java.lang.all; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a Byte. + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public class NumberToByteConverter : NumberToNumberConverter { + /** + * @param numberFormat + * @param fromType + * @param primitive + */ + public this(NumberFormat numberFormat, ClassInfo fromType, + bool primitive) { + super(numberFormat, fromType, (primitive) ? Byte.TYPE : Byte.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + if (StringToNumberParser.inByteRange(number)) { + return new Byte(number.byteValue()); + } + + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToDoubleConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToDoubleConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter; + +import java.lang.all; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a Double. + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public class NumberToDoubleConverter : NumberToNumberConverter { + + /** + * @param numberFormat + * @param fromType + * @param primitive + */ + public this(NumberFormat numberFormat, ClassInfo fromType, + bool primitive) { + super(numberFormat, fromType, (primitive) ? Double.TYPE : Double.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + if (StringToNumberParser.inDoubleRange(number)) { + return new Double(number.doubleValue()); + } + + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToFloatConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToFloatConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter; + +import java.lang.all; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a Float. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToFloatConverter : NumberToNumberConverter { + /** + * @param numberFormat + * @param fromType + * @param primitive + */ + public this(NumberFormat numberFormat, ClassInfo fromType, + bool primitive) { + super(numberFormat, fromType, (primitive) ? Float.TYPE : Float.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + if (StringToNumberParser.inFloatRange(number)) { + return new Float(number.floatValue()); + } + + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToIntegerConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToIntegerConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.IConverter; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a Integer. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToIntegerConverter : NumberToNumberConverter , + IConverter { + + /** + * @param numberFormat + * @param fromType + * @param primitive + */ + public this(NumberFormat numberFormat, + ClassInfo fromType, bool primitive) { + super(numberFormat, fromType, (primitive) ? Integer.TYPE : Integer.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + if (StringToNumberParser.inIntegerRange(number)) { + return new Integer(number.intValue()); + } + + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToLongConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToLongConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToLongConverter; + +import java.lang.all; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a Long. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToLongConverter : NumberToNumberConverter { + /** + * @param numberFormat + * @param fromType + * @param primitive + */ + public this(NumberFormat numberFormat, ClassInfo fromType, + bool primitive) { + super(numberFormat, fromType, (primitive) ? Long.TYPE : Long.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + if (StringToNumberParser.inLongRange(number)) { + return new Long(number.longValue()); + } + + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToNumberConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToNumberConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.Converter; + +import com.ibm.icu.text.NumberFormat; + +/** + * Base class for number to number converters. + *

+ * This class is thread safe. + *

+ * + * @since 1.0 + */ +public abstract class NumberToNumberConverter : Converter { + private NumberFormat numberFormat; + + private bool primitive; + + private String outOfRangeMessage; + + protected this(NumberFormat numberFormat, + ClassInfo fromType, ClassInfo toType) { + super(fromType, toType); + this.numberFormat = numberFormat; + this.primitive = toType.isPrimitive(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + */ + public final Object convert(Object fromObject) { + if (fromObject is null) { + if (primitive) { + throw new IllegalArgumentException( + "Parameter 'fromObject' cannot be null."); //$NON-NLS-1$ + } + + return null; + } + + if (!( null !is cast(Number)fromObject )) { + throw new IllegalArgumentException( + "Parameter 'fromObject' must be of type Number."); //$NON-NLS-1$ + } + + Number number = cast(Number) fromObject; + Number result = doConvert(number); + + if (result !is null) { + return result; + } + + synchronized (this) { + if (outOfRangeMessage is null) { + outOfRangeMessage = StringToNumberParser + .createOutOfRangeMessage(new Shortcast(Short.MIN_VALUE), + new Shortcast(Short.MAX_VALUE), numberFormat); + } + + throw new IllegalArgumentException(outOfRangeMessage); + } + } + + /** + * Invoked when the number should converted. + * + * @param number + * @return number if conversion was successfule, null if the + * number was out of range + */ + protected abstract Number doConvert(Number number); + + /** + * NumberFormat being used by the converter. Access to the format must be + * synchronized on the number format instance. + * + * @return number format + */ + public NumberFormat getNumberFormat() { + return numberFormat; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToShortConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToShortConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.NumberToShortConverter; + +import java.lang.all; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converts from a Number to a Short. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToShortConverter : NumberToNumberConverter { + /** + * @param numberFormat + * @param fromType + * @param primitive + */ + public this(NumberFormat numberFormat, ClassInfo fromType, + bool primitive) { + + super(numberFormat, fromType, (primitive) ? Short.TYPE : Short.classinfo); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter#doConvert(java.lang.Number) + */ + protected Number doConvert(Number number) { + if (StringToNumberParser.inShortRange(number)) { + return new Short(number.shortValue()); + } + + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/ObjectToStringConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/ObjectToStringConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,58 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + */ +module org.eclipse.core.internal.databinding.conversion.ObjectToStringConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.IConverter; + +/** + * Converts any object to a string by calling its toString() method. + */ +public class ObjectToStringConverter : IConverter { + private final ClassInfo fromClass; + + /** + * + */ + public this() { + this(Object.classinfo); + } + + /** + * @param fromClass + */ + public this(ClassInfo fromClass) { + this.fromClass = fromClass; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object) + */ + public Object convert(Object source) { + if (source is null) { + return ""; //$NON-NLS-1$ + } + return source.toString(); + } + + public Object getFromType() { + return fromClass; + } + + public Object getToType() { + return String.classinfo; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StatusToStringConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StatusToStringConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.StatusToStringConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.Converter; +import org.eclipse.core.databinding.conversion.IConverter; +import org.eclipse.core.runtime.IStatus; + +/** + * Converts an IStatus into a String. The message of the status is the returned value. + * + * @since 1.0 + */ +public class StatusToStringConverter : Converter , IConverter { + /** + * Constructs a new instance. + */ + public this() { + super(IStatus.classinfo, String.classinfo); + } + + /* (non-Javadoc) + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + */ + public Object convert(Object fromObject) { + if (fromObject is null) { + throw new IllegalArgumentException("Parameter 'fromObject' was null."); //$NON-NLS-1$ + } + + IStatus status = cast(IStatus) fromObject; + return status.getMessage(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,38 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + */ +module org.eclipse.core.internal.databinding.conversion.StringToBooleanConverter; + +import java.lang.all; + +/** + * StringToBooleanConverter. + */ +public class StringToBooleanConverter : StringToBooleanPrimitiveConverter { + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object) + */ + public Object convert(Object source) { + String sourceString = cast(String) source; + if ("".equals(sourceString.trim())) { //$NON-NLS-1$ + return null; + } + return super.convert(source); + } + + public Object getToType() { + return Boolean.classinfo; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + * Tom Schindl - bugfix for 217940 + */ +module org.eclipse.core.internal.databinding.conversion.StringToBooleanPrimitiveConverter; + +import java.lang.all; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.StringTokenizer; + +import org.eclipse.core.databinding.conversion.IConverter; +import org.eclipse.core.internal.databinding.BindingMessages; + +/** + * StringToBooleanPrimitiveConverter. + */ +public class StringToBooleanPrimitiveConverter : IConverter { + private static final String[] trueValues; + + private static final String[] falseValues; + + static this(){ + String delimiter = BindingMessages.getStringcast(BindingMessages.VALUE_DELIMITER); + String values = BindingMessages.getStringcast(BindingMessages.TRUE_STRING_VALUES); + trueValues = valuesToSortedArray(delimiter, values); + + values = BindingMessages.getStringcast(BindingMessages.FALSE_STRING_VALUES); + falseValues = valuesToSortedArray(delimiter, values); + } + + /** + * Returns a sorted array with all values converted to upper case. + * + * @param delimiter + * @param values + * @return sorted array of values + */ + private static String[] valuesToSortedArray(String delimiter, String values) { + List list = new LinkedList(); + StringTokenizer tokenizer = new StringTokenizer(values, delimiter); + while (tokenizer.hasMoreTokens()) { + list.add(tokenizer.nextToken().toUpperCase()); + } + + String[] array = cast(String[]) list.toArray(new String[list.size()]); + Arrays.sort(array); + + return array; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object) + */ + public Object convert(Object source) { + String s = cast(String) source; + s = s.toUpperCase(); + + if (Arrays.binarySearch(trueValues, s) > -1) { + return Boolean.TRUE; + } + + if (Arrays.binarySearch(falseValues, s) > -1) { + return Boolean.FALSE; + } + + throw new IllegalArgumentException(s ~ " is not a legal bool value"); //$NON-NLS-1$ + } + + public Object getFromType() { + return String.classinfo; + } + + public Object getToType() { + return Boolean.TYPE; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToByteConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToByteConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,90 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + */ +module org.eclipse.core.internal.databinding.conversion.StringToByteConverter; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser.ParseResult; +import org.eclipse.core.internal.databinding.validation.NumberFormatConverter; + +import com.ibm.icu.text.NumberFormat; + +/** + * @since 1.0 + */ +public class StringToByteConverter : NumberFormatConverter { + private String outOfRangeMessage; + private NumberFormat numberFormat; + private bool primitive; + + /** + * @param numberFormat + * @param toType + */ + private this(NumberFormat numberFormat, ClassInfo toType) { + super(String.classinfo, toType, numberFormat); + primitive = toType.isPrimitive(); + this.numberFormat = numberFormat; + } + + /** + * @param numberFormat + * @param primitive + * @return converter + */ + public static StringToByteConverter toByte(NumberFormat numberFormat, + bool primitive) { + return new StringToByteConverter(numberFormat, (primitive) ? Byte.TYPE : Byte.classinfo); + } + + /** + * @param primitive + * @return converter + */ + public static StringToByteConverter toByte(bool primitive) { + return toByte(NumberFormat.getIntegerInstance(), primitive); + } + + /* (non-Javadoc) + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + */ + public Object convert(Object fromObject) { + ParseResult result = StringToNumberParser.parse(fromObject, + numberFormat, primitive); + + if (result.getPosition() !is null) { + // this shouldn't happen in the pipeline as validation should catch + // it but anyone can call convert so we should return a properly + // formatted message in an exception + throw new IllegalArgumentException(StringToNumberParser + .createParseErrorMessage(cast(String) fromObject, result + .getPosition())); + } else if (result.getNumber() is null) { + // if an error didn't occur and the number is null then it's a boxed + // type and null should be returned + return null; + } + + if (StringToNumberParser.inByteRange(result.getNumber())) { + return new Byte(result.getNumber().byteValue()); + } + + synchronized (this) { + if (outOfRangeMessage is null) { + outOfRangeMessage = StringToNumberParser + .createOutOfRangeMessage(new Bytecast(Byte.MIN_VALUE), new Bytecast(Byte.MAX_VALUE), numberFormat); + } + + throw new IllegalArgumentException(outOfRangeMessage); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToCharacterConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToCharacterConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,83 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + * Matt Carter - Improved primitive conversion support (bug 197679) + */ +module org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.IConverter; + +/** + * StringToCharacterConverter. + */ +public class StringToCharacterConverter : IConverter { + + private final bool primitiveTarget; + + /** + * + * @param primitiveTarget + */ + public this(bool primitiveTarget) { + this.primitiveTarget = primitiveTarget; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.binding.converter.IConverter#convert(java.lang.Object) + */ + public Object convert(Object source) { + if (source !is null && !( null !is cast(String)source )) + throw new IllegalArgumentException( + "String2Character: Expected type String, got type [" + source.getClass().getName() + "]"); //$NON-NLS-1$ //$NON-NLS-2$ + + String s = cast(String) source; + if (source is null || s.equals("")) { //$NON-NLS-1$ + if (primitiveTarget) + throw new IllegalArgumentException( + "String2Character: cannot convert null/empty string to character primitive"); //$NON-NLS-1$ + return null; + } + Character result; + + if (s.length() > 1) + throw new IllegalArgumentException( + "String2Character: string too long: " + s); //$NON-NLS-1$ + + try { + result = new Character(s.charAt(0)); + } catch (Exception e) { + throw new IllegalArgumentException( + "String2Character: " + e.getMessage() + ": " + s); //$NON-NLS-1$ //$NON-NLS-2$ + } + + return result; + } + + public Object getFromType() { + return String.classinfo; + } + + public Object getToType() { + return primitiveTarget ? Character.TYPE : Character.classinfo; + } + + /** + * @param primitive + * @return converter + */ + public static StringToCharacterConverter toCharacter(bool primitive) { + return new StringToCharacterConverter(primitive); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,38 @@ +/* + * Copyright cast(C) 2005 db4objects Inc. http://www.db4o.com + * + * 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: + * db4objects - Initial API and implementation + */ +module org.eclipse.core.internal.databinding.conversion.StringToDateConverter; + +import java.lang.all; + +import java.util.Date; + +import org.eclipse.core.databinding.conversion.IConverter; + + +/** + * Convert a String to a java.util.Date, respecting the current locale + * + * @since 1.0 + */ +public class StringToDateConverter : DateConversionSupport , IConverter { + public Object convert(Object source) { + return parse(source.toString()); + } + + public Object getFromType() { + return String.classinfo; + } + + public Object getToType() { + return Date.classinfo; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +import java.lang.all; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.text.ParsePosition; + +import org.eclipse.core.internal.databinding.BindingMessages; + +import com.ibm.icu.text.NumberFormat; + +/** + * Utility class for the parsing of strings to numbers. + * + * @since 1.0 + */ +public class StringToNumberParser { + private static final BigDecimal FLOAT_MAX_BIG_DECIMAL = new BigDecimal( + Float.MAX_VALUE); + private static final BigDecimal FLOAT_MIN_BIG_DECIMAL = new BigDecimal( + -Float.MAX_VALUE); + + private static final BigDecimal DOUBLE_MAX_BIG_DECIMAL = new BigDecimal( + Double.MAX_VALUE); + private static final BigDecimal DOUBLE_MIN_BIG_DECIMAL = new BigDecimal( + -Double.MAX_VALUE); + + /** + * @param value + * @param numberFormat + * @param primitive + * @return result + */ + public static ParseResult parse(Object value, NumberFormat numberFormat, + bool primitive) { + if (!( null !is cast(String)value )) { + throw new IllegalArgumentException( + "Value to convert is not a String"); //$NON-NLS-1$ + } + + String source = cast(String) value; + ParseResult result = new ParseResult(); + if (!primitive && source.trim().length() is 0) { + return result; + } + + synchronized (numberFormat) { + ParsePosition position = new ParsePosition(0); + Number parseResult = null; + parseResult = numberFormat.parse(source, position); + + if (position.getIndex() !is source.length() + || position.getErrorIndex() > -1) { + + result.position = position; + } else { + result.number = parseResult; + } + } + + return result; + } + + /** + * The result of a parse operation. + * + * @since 1.0 + */ + public static class ParseResult { + /* package */Number number; + /* package */ParsePosition position; + + /** + * The number as a result of the conversion. null if the + * value could not be converted or if the type is not a primitive and + * the value was an empty string. + * + * @return number + */ + public Number getNumber() { + return number; + } + + /** + * ParsePosition if an error occurred while parsing. null + * if no error occurred. + * + * @return parse position + */ + public ParsePosition getPosition() { + return position; + } + } + + /** + * Formats an appropriate message for a parsing error. + * + * @param value + * @param position + * @return message + */ + public static String createParseErrorMessage(String value, + ParsePosition position) { + int errorIndex = (position.getErrorIndex() > -1) ? position + .getErrorIndex() : position.getIndex(); + + if (errorIndex < value.length()) { + return BindingMessages.formatString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR, + [cast(Object) stringcast(value), new Integer(errorIndex + 1), + new Character(value.charAt(errorIndex)) ]); + } + return BindingMessages.formatString(BindingMessages.VALIDATE_NUMBER_PARSE_ERROR_NO_CHARACTER, + [cast(Object) stringcast(value), new Integer(errorIndex + 1) ]); + } + + /** + * Formats an appropriate message for an out of range error. + * + * @param minValue + * @param maxValue + * @param numberFormat when accessed method synchronizes on instance + * @return message + */ + public static String createOutOfRangeMessage(Number minValue, + Number maxValue, NumberFormat numberFormat) { + String min = null; + String max = null; + + synchronized (numberFormat) { + min = numberFormat.format(minValue); + max = numberFormat.format(maxValue); + } + + return BindingMessages.formatString( + "Validate_NumberOutOfRangeError", [ cast(Object)min, max ]); //$NON-NLS-1$ + } + + /** + * Returns true if the provided number is in + * the range of a integer. + * + * @param number + * @return true if a valid integer + * @throws IllegalArgumentException + * if the number type is unsupported + */ + public static bool inIntegerRange(Number number) { + return checkInteger(number, 31); + } + + /** + * Validates the range of the provided number. + * + * @param number + * @param bitLength number of bits allowed to be in range + * @return true if in range + */ + private static bool checkInteger(Number number, int bitLength) { + BigInteger bigInteger = null; + + if ( null !is cast(Integer )number || null !is cast(Long)number ) { + bigInteger = BigInteger.valueOf(number.longValue()); + } else if ( null !is cast(Float )number || null !is cast(Double)number ) { + double doubleValue = number.doubleValue(); + /* + * doubleValue is doubleValue is used to check for NaN because NaN !is + * NaN. The only way to check for NaN is to compare that the value + * is equal to itself. + */ + if (doubleValue is doubleValue + && doubleValue !is Double.NEGATIVE_INFINITY + && doubleValue !is Double.POSITIVE_INFINITY) { + bigInteger = (new BigDecimal(doubleValue)).toBigInteger(); + } else { + return false; + } + } else if ( null !is cast(BigInteger)number ) { + bigInteger = cast(BigInteger) number; + } else if ( null !is cast(BigDecimal)number ) { + bigInteger = (cast(BigDecimal) number).toBigInteger(); + } else { + /* + * The else is necessary as the ICU4J plugin has it's own BigDecimal + * implementation which isn't part of the replacement plugin. So + * that this will work we fall back on the double value of the + * number. + */ + bigInteger = (new BigDecimal(number.doubleValue())).toBigInteger(); + } + + if (bigInteger !is null) { + return bigInteger.bitLength() <= bitLength; + } + + throw new IllegalArgumentException( + "Number of type [" + number.getClass().getName() + "] is not supported."); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Returns true if the provided number is in + * the range of a long. + * + * @param number + * @return true if in range + * @throws IllegalArgumentException + * if the number type is unsupported + */ + public static bool inLongRange(Number number) { + return checkInteger(number, 63); + } + + /** + * Returns true if the provided number is in + * the range of a float. + * + * @param number + * @return true if in range + * @throws IllegalArgumentException + * if the number type is unsupported + */ + public static bool inFloatRange(Number number) { + return checkDecimal(number, FLOAT_MIN_BIG_DECIMAL, FLOAT_MAX_BIG_DECIMAL); + } + + private static bool checkDecimal(Number number, BigDecimal min, BigDecimal max) { + BigDecimal bigDecimal = null; + if ( null !is cast(Integer )number || null !is cast(Long)number ) { + bigDecimal = new BigDecimal(number.doubleValue()); + } else if ( null !is cast(Float )number || null !is cast(Double)number ) { + double doubleValue = number.doubleValue(); + + /* + * doubleValue is doubleValue is used to check for NaN because NaN !is + * NaN. The only way to check for NaN is to compare that the value + * is equal to itself. + */ + if (doubleValue is doubleValue + && doubleValue !is Double.NEGATIVE_INFINITY + && doubleValue !is Double.POSITIVE_INFINITY) { + bigDecimal = new BigDecimal(doubleValue); + } else { + return false; + } + } else if ( null !is cast(BigInteger)number ) { + bigDecimal = new BigDecimal(cast(BigInteger) number); + } else if ( null !is cast(BigDecimal)number ) { + bigDecimal = cast(BigDecimal) number; + } else { + /* + * The else is necessary as the ICU4J plugin has it's own BigDecimal + * implementation which isn't part of the replacement plugin. So + * that this will work we fall back on the double value of the + * number. + */ + bigDecimal = new BigDecimal(number.doubleValue()); + } + + if (bigDecimal !is null) { + return max.compareTo(bigDecimal) >= 0 + && min.compareTo(bigDecimal) <= 0; + } + + throw new IllegalArgumentException( + "Number of type [" + number.getClass().getName() + "] is not supported."); //$NON-NLS-1$ //$NON-NLS-2$ + } + + /** + * Returns true if the provided number is in + * the range of a double. + * + * @param number + * @return true if in range + * @throws IllegalArgumentException + * if the number type is unsupported + */ + public static bool inDoubleRange(Number number) { + return checkDecimal(number, DOUBLE_MIN_BIG_DECIMAL, DOUBLE_MAX_BIG_DECIMAL); + } + + /** + * Returns true if the provided number is in + * the range of a short. + * + * @param number + * @return true if in range + */ + public static bool inShortRange(Number number) { + return checkInteger(number, 15); + } + + /** + * Returns true if the provided number is in + * the range of a byte. + * + * @param number + * @return true if in range + */ + public static bool inByteRange(Number number) { + return checkInteger(number, 7); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToShortConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToShortConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.conversion.StringToShortConverter; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser.ParseResult; +import org.eclipse.core.internal.databinding.validation.NumberFormatConverter; + +import com.ibm.icu.text.NumberFormat; + +/** + * @since 1.0 + */ +public class StringToShortConverter : NumberFormatConverter { + private final NumberFormat numberFormat; + private final bool primitive; + + private String outOfRangeMessage; + + /** + * Constructs a new instance. + */ + private this(NumberFormat numberFormat, ClassInfo toType) { + super(String.classinfo, toType, numberFormat); + this.numberFormat = numberFormat; + primitive = toType.isPrimitive(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.conversion.IConverter#convert(java.lang.Object) + */ + public Object convert(Object fromObject) { + ParseResult result = StringToNumberParser.parse(fromObject, + numberFormat, primitive); + + if (result.getPosition() !is null) { + // this shouldn't happen in the pipeline as validation should catch + // it but anyone can call convert so we should return a properly + // formatted message in an exception + throw new IllegalArgumentException(StringToNumberParser + .createParseErrorMessage(cast(String) fromObject, result + .getPosition())); + } else if (result.getNumber() is null) { + // if an error didn't occur and the number is null then it's a boxed + // type and null should be returned + return null; + } + + if (StringToNumberParser.inShortRange(result.getNumber())) { + return new Short(result.getNumber().shortValue()); + } + + synchronized (this) { + if (outOfRangeMessage is null) { + outOfRangeMessage = StringToNumberParser + .createOutOfRangeMessage(new Shortcast(Short.MIN_VALUE), new Shortcast(Short.MAX_VALUE), numberFormat); + } + + throw new IllegalArgumentException(outOfRangeMessage); + } + } + + /** + * @param primitive + * true if the convert to type is a short + * @return to Short converter for the default locale + */ + public static StringToShortConverter toShort(bool primitive) { + return toShort(NumberFormat.getIntegerInstance(), primitive); + } + + /** + * @param numberFormat + * @param primitive + * @return to Short converter with the provided numberFormat + */ + public static StringToShortConverter toShort(NumberFormat numberFormat, + bool primitive) { + return new StringToShortConverter(numberFormat, + (primitive) ? Short.TYPE : Short.classinfo); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/messages.properties --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/messages.properties Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,56 @@ +############################################################################### +# Copyright (c) 2000, 2008 IBM Corporation and others. +# 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: +# IBM Corporation - initial API and implementation +############################################################################### +# NLS file for JFace Data Binding +############################################################################### +## Uneeded value +# +#Yes=Yes +#yes=yes +#No=No +#no=no +#True=True +#true=true +#False=False +#false=false +#and=and +#or=or +#Validate_BooleanHelp=Please type "Yes", "No", "True", or "False" +# +#Validate_RangeStart=Please enter a number between +#Validate_Like=Please enter a number like +# +############### + +IndexOutOfRange=Index out of Range. +MultipleProblems=Multiple Problems. + +ValueBinding_ErrorWhileSettingValue=An error occurred while setting the value. +DateFormat_DateTime=dd.MM.yyyy HH:mm:ss.SSS Z +DateFormat_Time=HH:mm:ss.SSS + +#ValueDelimiter should be used to separate multiple values that are stored in one key +ValueDelimiter=, + +#Values must be separated by ValueDelimiter +TrueStringValues=yes,true +FalseStringValues=no,false + +Validate_NumberOutOfRangeError=Please enter a value between [{0}] and [{1}] and with a similar format. +Validate_NumberParseError=Invalid character for value [{0}] at position [{1}] character [{2}]. +Validate_NumberParseErrorNoCharacter=Missing character for value [{0}] at position [{1}]. + +Validate_ConversionToPrimitive="Null object values can not be converted to primitives." +Validate_ConversionFromClassToPrimitive="Wrong object type to convert to primitive." + +Validate_NoChangeAllowedHelp=Changes are not allowed in this field +Validate_CharacterHelp=Please type a character + +Examples=Examples \ No newline at end of file diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ConstantObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ConstantObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,112 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007-2008 Matt Carter and others. + * 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: + * Matt Carter - initial API and implementation (bug 212518) + * Matthew Hall - bug 212518 + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.ConstantObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.WritableValue; +import org.eclipse.core.runtime.Assert; + +/** + * An immutable {@link IObservableValue}. + * + * @see WritableValue + */ +public class ConstantObservableValue : IObservableValue { + final Realm realm; + final Object value; + final Object type; + + /** + * Construct a constant value of the given type, in the default realm. + * + * @param value + * immutable value + * @param type + * type + */ + public this(Object value, Object type) { + this(Realm.getDefault(), value, type); + } + + /** + * Construct a constant value of the given type, in the given realm. + * + * @param realm + * Realm + * @param value + * immutable value + * @param type + * type + */ + public this(Realm realm, Object value, Object type) { + Assert.isNotNull(realm, "Realm cannot be null"); //$NON-NLS-1$ + this.realm = realm; + this.value = value; + this.type = type; + } + + public Object getValueType() { + return type; + } + + public Object getValue() { + ObservableTracker.getterCalled(this); + return value; + } + + public void setValue(Object value) { + throw new UnsupportedOperationException(); + } + + public void addValueChangeListener(IValueChangeListener listener) { + // ignore + } + + public void removeValueChangeListener(IValueChangeListener listener) { + // ignore + } + + public void addChangeListener(IChangeListener listener) { + // ignore + } + + public void addStaleListener(IStaleListener listener) { + // ignore + } + + public void dispose() { + // nothing to dispose + } + + public Realm getRealm() { + return realm; + } + + public bool isStale() { + return false; + } + + public void removeChangeListener(IChangeListener listener) { + // ignore + } + + public void removeStaleListener(IStaleListener listener) { + // ignore + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/EmptyObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/EmptyObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208858 + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.EmptyObservableList; + +import java.lang.all; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.runtime.Assert; + +/** + * Singleton empty list + */ +public class EmptyObservableList : IObservableList { + + private static final List emptyList = Collections.EMPTY_LIST; + + private Realm realm; + private Object elementType; + + /** + * Creates an empty list. This list may be disposed multiple times + * without any side-effects. + * + * @param realm + * the realm of the constructed list + */ + public this(Realm realm) { + this(realm, null); + } + + /** + * Creates an empty list. This list may be disposed multiple times + * without any side-effects. + * + * @param realm + * the realm of the constructed list + * @param elementType + * the element type of the constructed list + * @since 1.1 + */ + public this(Realm realm, Object elementType) { + this.realm = realm; + this.elementType = elementType; + } + + public void addListChangeListener(IListChangeListener listener) { + // ignore + } + + public void removeListChangeListener(IListChangeListener listener) { + // ignore + } + + public Object getElementType() { + return elementType; + } + + public int size() { + checkRealm(); + return 0; + } + + void checkRealm() { + Assert.isTrue(realm.isCurrent(), + "Observable cannot be accessed outside its realm"); //$NON-NLS-1$ + } + + public bool isEmpty() { + checkRealm(); + return true; + } + + public bool contains(Object o) { + checkRealm(); + return false; + } + + public Iterator iterator() { + checkRealm(); + return emptyList.iterator(); + } + + public Object[] toArray() { + checkRealm(); + return emptyList.toArray(); + } + + public Object[] toArray(Object[] a) { + return emptyList.toArray(a); + } + + public bool add(Object o) { + throw new UnsupportedOperationException(); + } + + public bool remove(Object o) { + throw new UnsupportedOperationException(); + } + + public bool containsAll(Collection c) { + checkRealm(); + return c.isEmpty(); + } + + public bool addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public void addChangeListener(IChangeListener listener) { + } + + public void removeChangeListener(IChangeListener listener) { + } + + public void addStaleListener(IStaleListener listener) { + } + + public void removeStaleListener(IStaleListener listener) { + } + + public bool isStale() { + checkRealm(); + return false; + } + + public void dispose() { + } + + public bool addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + + public Object get(int index) { + return emptyList.get(index); + } + + public int indexOf(Object o) { + return -1; + } + + public int lastIndexOf(Object o) { + return -1; + } + + public ListIterator listIterator() { + return emptyList.listIterator(); + } + + public ListIterator listIterator(int index) { + return emptyList.listIterator(index); + } + + public Object remove(int index) { + throw new UnsupportedOperationException(); + } + + public Object set(int index, Object element) { + throw new UnsupportedOperationException(); + } + + public Object move(int oldIndex, int newIndex) { + throw new UnsupportedOperationException(); + } + + public List subList(int fromIndex, int toIndex) { + return emptyList.subList(fromIndex, toIndex); + } + + public void add(int index, Object o) { + throw new UnsupportedOperationException(); + } + + public Realm getRealm() { + return realm; + } + + public override bool opEquals(Object obj) { + checkRealm(); + if (obj is this) + return true; + if (obj is null) + return false; + if (!( null !is cast(List)obj )) + return false; + + return (cast(List) obj).isEmpty(); + } + + public int hashCode() { + checkRealm(); + return 1; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/EmptyObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/EmptyObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.EmptyObservableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.runtime.Assert; + +/** + * Singleton empty set + */ +public class EmptyObservableSet : IObservableSet { + + private static final Set emptySet = Collections.EMPTY_SET; + + private Realm realm; + private Object elementType; + + /** + * Creates a singleton empty set. This set may be disposed multiple times + * without any side-effects. + * + * @param realm + * the realm of the constructed set + */ + public this(Realm realm) { + this(realm, null); + } + + /** + * Creates a singleton empty set. This set may be disposed multiple times + * without any side-effects. + * + * @param realm + * the realm of the constructed set + * @param elementType + * the element type of the constructed set + * @since 1.1 + */ + public this(Realm realm, Object elementType) { + this.realm = realm; + this.elementType = elementType; + } + + public void addSetChangeListener(ISetChangeListener listener) { + } + + public void removeSetChangeListener(ISetChangeListener listener) { + } + + public Object getElementType() { + return elementType; + } + + public int size() { + checkRealm(); + return 0; + } + + private void checkRealm() { + Assert.isTrue(realm.isCurrent(), + "Observable cannot be accessed outside its realm"); //$NON-NLS-1$ + } + + public bool isEmpty() { + checkRealm(); + return true; + } + + public bool contains(Object o) { + checkRealm(); + return false; + } + + public Iterator iterator() { + checkRealm(); + return emptySet.iterator(); + } + + public Object[] toArray() { + checkRealm(); + return emptySet.toArray(); + } + + public Object[] toArray(Object[] a) { + return emptySet.toArray(a); + } + + public bool add(Object o) { + throw new UnsupportedOperationException(); + } + + public bool remove(Object o) { + throw new UnsupportedOperationException(); + } + + public bool containsAll(Collection c) { + checkRealm(); + return c.isEmpty(); + } + + public bool addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public bool removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public void addChangeListener(IChangeListener listener) { + } + + public void removeChangeListener(IChangeListener listener) { + } + + public void addStaleListener(IStaleListener listener) { + } + + public void removeStaleListener(IStaleListener listener) { + } + + public bool isStale() { + checkRealm(); + return false; + } + + public void dispose() { + } + + public Realm getRealm() { + return realm; + } + + public override bool opEquals(Object obj) { + checkRealm(); + if (obj is this) + return true; + if (obj is null) + return false; + if (!( null !is cast(Set)obj )) + return false; + + return (cast(Set) obj).isEmpty(); + } + + public int hashCode() { + checkRealm(); + return 0; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/IStalenessConsumer.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/IStalenessConsumer.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.IStalenessConsumer; + +import java.lang.all; + +/** + * @since 1.0 + * + */ +public interface IStalenessConsumer { + /** + * @param stale + * + */ + public void setStale(bool stale); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/MapEntryObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/MapEntryObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2008 Marko Topolnik and others. + * 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: + * Marko Topolnik - initial API and implementation (bug 184830) + * Matthew Hall - bug 184830 + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.MapEntryObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.map.IMapChangeListener; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.map.MapChangeEvent; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; +import org.eclipse.core.databinding.observable.value.IObservableValue; + +/** + * An {@link IObservableValue} that tracks the value of an entry in an + * {@link IObservableMap}, identified by the entry's key. + * + * @since 1.1 + */ +public class MapEntryObservableValue : AbstractObservableValue { + private IObservableMap map; + private Object key; + private Object valueType; + + private IMapChangeListener changeListener = new class() IMapChangeListener { + public void handleMapChange(MapChangeEvent event) { + if (event.diff.getAddedKeys().contains(key)) { + final Object newValue = event.diff.getNewValue(key); + if (newValue !is null) { + fireValueChange(Diffs.createValueDiff(null, newValue)); + } + } else if (event.diff.getChangedKeys().contains(key)) { + fireValueChange(Diffs.createValueDiff(event.diff + .getOldValue(key), event.diff.getNewValue(key))); + } else if (event.diff.getRemovedKeys().contains(key)) { + final Object oldValue = event.diff.getOldValue(key); + if (oldValue !is null) { + fireValueChange(Diffs.createValueDiff(oldValue, null)); + } + } + } + }; + + private IStaleListener staleListener = new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + fireStale(); + } + }; + + /** + * Creates a map entry observable. + * + * @param map + * the observable map whose entry will be tracked + * @param key + * the key identifying the entry whose value will be tracked + * @param valueType + * the type of the value + */ + public this(IObservableMap map, Object key, + Object valueType) { + super(map.getRealm()); + this.map = map; + this.key = key; + this.valueType = valueType; + + map.addMapChangeListener(changeListener); + map.addStaleListener(staleListener); + } + + public Object getValueType() { + return this.valueType; + } + + public bool isStale() { + ObservableTracker.getterCalled(this); + return map.isStale(); + } + + public synchronized void dispose() { + if (map !is null) { + map.removeMapChangeListener(changeListener); + map.removeStaleListener(staleListener); + map = null; + changeListener = null; + staleListener = null; + } + super.dispose(); + } + + protected Object doGetValue() { + return this.map.get(this.key); + } + + protected void doSetValue(Object value) { + this.map.put(this.key, value); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ProxyObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ProxyObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2007 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 208332) + * IBM Corporation - initial API and implementation + * (through ProxyObservableSet.java) + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.ProxyObservableList; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ObservableList; + +/** + * Wraps an observable list. This object acts like an exact copy of the original + * list, and tracks all the changes in the original. The only difference is that + * disposing the wrapper will not dispose the original. You can use this + * whenever you need to return an IObservableList from a method that expects the + * caller to dispose the list, but you have an IObservableList that you don't + * want disposed. + * + * @since 1.1 + */ +public class ProxyObservableList : ObservableList { + private IListChangeListener listChangelistener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + fireListChange(event.diff); + } + }; + + private IStaleListener staleListener = new class() IStaleListener { + public void handleStale(StaleEvent event) { + fireStale(); + } + }; + + private IObservableList wrappedList; + + /** + * Constructs a ProxyObservableList that tracks the state of the given list. + * + * @param wrappedList + * the list being wrapped + */ + public this(IObservableList wrappedList) { + super(wrappedList.getRealm(), wrappedList, wrappedList.getElementType()); + this.wrappedList = wrappedList; + wrappedList.addListChangeListener(listChangelistener); + wrappedList.addStaleListener(staleListener); + } + + public bool isStale() { + getterCalled(); + return wrappedList is null ? false : wrappedList.isStale(); + } + + public void dispose() { + if (wrappedList !is null) { + wrappedList.removeListChangeListener(listChangelistener); + listChangelistener = null; + wrappedList.removeStaleListener(staleListener); + staleListener = null; + wrappedList = null; + } + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ProxyObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ProxyObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208332 + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.ProxyObservableSet; + +import java.lang.all; + +import java.util.Collections; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.set.AbstractObservableSet; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; + +/** + * Wraps an observable set. This object acts like an exact copy of the original + * set, and tracks all the changes in the original. The only difference is that + * disposing the wrapper will not dispose the original. You can use this + * whenever you need to return an IObservableSet from a method that expects the + * caller to dispose the set, but you have an IObservableSet that you don't want + * disposed. + */ +public class ProxyObservableSet : AbstractObservableSet { + private IObservableSet wrappedSet; + private Object elementType; + + private ISetChangeListener setChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + fireSetChange(event.diff); + } + }; + + private IStaleListener staleListener = new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + fireStale(); + } + }; + + /** + * Constructs a ProxyObservableSet that tracks the state of the given set. + * + * @param wrappedSet + * the set being wrapped + */ + public this(IObservableSet wrappedSet) { + super(wrappedSet.getRealm()); + this.wrappedSet = wrappedSet; + this.elementType = wrappedSet.getElementType(); + wrappedSet.addSetChangeListener(setChangeListener); + wrappedSet.addStaleListener(staleListener); + } + + protected Set getWrappedSet() { + return wrappedSet is null ? Collections.EMPTY_SET : wrappedSet; + } + + public Object getElementType() { + return elementType; + } + + public bool isStale() { + getterCalled(); + return wrappedSet is null ? false : wrappedSet.isStale(); + } + + public void dispose() { + if (wrappedSet !is null) { + wrappedSet.removeSetChangeListener(setChangeListener); + setChangeListener = null; + wrappedSet.removeStaleListener(staleListener); + staleListener = null; + wrappedSet = null; + } + elementType = null; + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/StalenessObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/StalenessObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * Boris Bokowski, IBM Corporation - initial API and implementation + * Matthew Hall - bug 212468 + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.StalenessObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; + +/** + * An observable value that tracks the staleness of an {@link IObservable}. + * + * @since 1.1 + */ +public class StalenessObservableValue : AbstractObservableValue { + + private class MyListener : IChangeListener, IStaleListener { + public void handleChange(ChangeEvent event) { + if (stale && !event.getObservable().isStale()) { + stale = false; + fireValueChange(Diffs.createValueDiff(Boolean.TRUE, + Boolean.FALSE)); + } + } + + public void handleStale(StaleEvent staleEvent) { + if (!stale) { + stale = true; + fireValueChange(Diffs.createValueDiff(Boolean.FALSE, + Boolean.TRUE)); + } + } + } + + private IObservable tracked; + private bool stale; + private MyListener listener = new MyListener(); + + /** + * Constructs a StalenessObservableValue that tracks the staleness of the + * given {@link IObservable}. + * + * @param observable + * the observable to track + */ + public this(IObservable observable) { + super(observable.getRealm()); + this.tracked = observable; + this.stale = observable.isStale(); + tracked.addChangeListener(listener); + tracked.addStaleListener(listener); + } + + protected Object doGetValue() { + return tracked.isStale() ? Boolean.TRUE : Boolean.FALSE; + } + + public Object getValueType() { + return Boolean.TYPE; + } + + public synchronized void dispose() { + if (tracked !is null) { + tracked.removeChangeListener(listener); + tracked.removeStaleListener(listener); + tracked = null; + listener = null; + } + super.dispose(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/StalenessTracker.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/StalenessTracker.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.StalenessTracker; + +import java.lang.all; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.internal.databinding.IdentityWrapper; + +/** + * @since 1.0 + * + */ +public class StalenessTracker { + + private Map staleMap = new HashMap(); + + private int staleCount = 0; + + private final IStalenessConsumer stalenessConsumer; + + private class ChildListener : IStaleListener, IChangeListener { + public void handleStale(StaleEvent event) { + processStalenessChange(cast(IObservable) event.getSource(), true); + } + + public void handleChange(ChangeEvent event) { + processStalenessChange(cast(IObservable) event.getSource(), true); + } + } + + private ChildListener childListener = new ChildListener(); + + /** + * @param observables + * @param stalenessConsumer + */ + public this(IObservable[] observables, + IStalenessConsumer stalenessConsumer) { + this.stalenessConsumer = stalenessConsumer; + for (int i = 0; i < observables.length; i++) { + IObservable observable = observables[i]; + doAddObservable(observable, false); + } + stalenessConsumer.setStale(staleCount > 0); + } + + /** + * @param child + * @param callback + */ + public void processStalenessChange(IObservable child, bool callback) { + bool oldStale = staleCount > 0; + IdentityWrapper wrappedChild = new IdentityWrapper(child); + bool oldChildStale = getOldChildStale(wrappedChild); + bool newChildStale = child.isStale(); + if (oldChildStale !is newChildStale) { + if (oldChildStale) { + staleCount--; + } else { + staleCount++; + } + staleMap.put(wrappedChild, newChildStale ? Boolean.TRUE : Boolean.FALSE); + } + bool newStale = staleCount > 0; + if (callback && (newStale !is oldStale)) { + stalenessConsumer.setStale(newStale); + } + } + + /** + * @param wrappedChild + */ + private bool getOldChildStale(IdentityWrapper wrappedChild) { + Object oldChildValue = staleMap.get(wrappedChild); + bool oldChildStale = oldChildValue is null ? false + : (cast(Boolean) oldChildValue).booleanValue(); + return oldChildStale; + } + + /** + * @param observable + */ + public void addObservable(IObservable observable) { + doAddObservable(observable, true); + } + + private void doAddObservable(IObservable observable, bool callback) { + processStalenessChange(observable, callback); + observable.addChangeListener(childListener); + observable.addStaleListener(childListener); + } + + /** + * @param observable + */ + public void removeObservable(IObservable observable) { + bool oldStale = staleCount > 0; + IdentityWrapper wrappedChild = new IdentityWrapper(observable); + bool oldChildStale = getOldChildStale(wrappedChild); + if (oldChildStale) { + staleCount--; + } + staleMap.remove(wrappedChild); + observable.removeChangeListener(childListener); + observable.removeStaleListener(childListener); + bool newStale = staleCount > 0; + if (newStale !is oldStale) { + stalenessConsumer.setStale(newStale); + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2006-2008 Cerner Corporation and others. + * 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: + * Brad Reynolds - initial API and implementation + * Matthew Hall - bug 208332 + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.UnmodifiableObservableList; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ObservableList; + +/** + * ObservableList implementation that prevents modification by consumers. Events + * in the originating wrapped list are propagated and thrown from this instance + * when appropriate. All mutators throw an UnsupportedOperationException. + * + * @since 1.0 + */ +/* + * Implementation makes the assumption that the superclass cast(ObservableList) is + * unmodifiable and that all modify methods throw an + * UnsupportedOperationException. + */ +public class UnmodifiableObservableList : ObservableList { + /** + * List that is being made unmodifiable. + */ + private IObservableList wrappedList; + + private IListChangeListener listChangeListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + // Fires a Change and then ListChange event. + fireListChange(event.diff); + } + }; + + private IStaleListener staleListener = new class() IStaleListener { + public void handleStale(StaleEvent event) { + fireStale(); + } + }; + + /** + * @param wrappedList + */ + public this(IObservableList wrappedList) { + super(wrappedList.getRealm(), wrappedList, wrappedList.getElementType()); + this.wrappedList = wrappedList; + + wrappedList.addListChangeListener(listChangeListener); + + wrappedList.addStaleListener(staleListener); + } + + /** + * Because this instance is immutable staleness cannot be changed. + * + * @throws UnsupportedOperationException + * because this instance is unmodifiable. + */ + public void setStale(bool stale) { + throw new UnsupportedOperationException(); + } + + public bool isStale() { + getterCalled(); + return wrappedList is null ? false : wrappedList.isStale(); + } + + public synchronized void dispose() { + if (wrappedList !is null) { + wrappedList.removeListChangeListener(listChangeListener); + wrappedList.removeStaleListener(staleListener); + wrappedList = null; + } + listChangeListener = null; + staleListener = null; + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2007 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 208332) + * Brad Reynolds - initial API and implementation + * (through UnmodifiableObservableList.java) + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.UnmodifiableObservableSet; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.ObservableSet; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; + +/** + * ObservableList implementation that prevents modification by consumers. Events + * in the originating wrapped list are propagated and thrown from this instance + * when appropriate. All mutators throw an UnsupportedOperationException. + * + * @since 1.1 + */ +public class UnmodifiableObservableSet : ObservableSet { + private ISetChangeListener setChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + fireSetChange(event.diff); + } + }; + + private IStaleListener staleListener = new class() IStaleListener { + public void handleStale(StaleEvent event) { + fireStale(); + } + }; + + private IObservableSet wrappedSet; + + /** + * @param wrappedSet + */ + public this(IObservableSet wrappedSet) { + super(wrappedSet.getRealm(), wrappedSet, wrappedSet.getElementType()); + + this.wrappedSet = wrappedSet; + + wrappedSet.addSetChangeListener(setChangeListener); + wrappedSet.addStaleListener(staleListener); + } + + /** + * Because this instance is immutable staleness cannot be changed. + */ + public void setStale(bool stale) { + throw new UnsupportedOperationException(); + } + + public bool isStale() { + getterCalled(); + return wrappedSet is null ? false : wrappedSet.isStale(); + } + + public synchronized void dispose() { + if (wrappedSet !is null) { + wrappedSet.removeSetChangeListener(setChangeListener); + wrappedSet.removeStaleListener(staleListener); + wrappedSet = null; + } + setChangeListener = null; + staleListener = null; + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 219909) + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.UnmodifiableObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; + +/** + * An unmodifiable wrapper class for IObservableValue instances. + * @since 1.1 + */ +public class UnmodifiableObservableValue : AbstractObservableValue { + private IObservableValue wrappedValue; + + /** + * Constructs an UnmodifiableObservableValue which wraps the given + * observable value + * + * @param wrappedValue + * the observable value to wrap in an unmodifiable instance. + */ + public this(IObservableValue wrappedValue) { + super(wrappedValue.getRealm()); + + this.wrappedValue = wrappedValue; + wrappedValue.addValueChangeListener(new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + fireValueChange(event.diff); + } + }); + wrappedValue.addStaleListener(new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + fireStale(); + } + }); + } + + protected Object doGetValue() { + return wrappedValue.getValue(); + } + + public Object getValueType() { + return wrappedValue.getValueType(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,397 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 218269) + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.ValidatedObservableList; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiff; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; +import org.eclipse.core.databinding.observable.list.ListDiffVisitor; +import org.eclipse.core.databinding.observable.list.ObservableList; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 3.3 + * + */ +public class ValidatedObservableList : ObservableList { + private IObservableList target; + private IObservableValue validationStatus; + + // Only true when out of sync with target due to validation status + private bool stale; + + // True when validaton status changes from invalid to valid. + private bool computeNextDiff = false; + + private bool updatingTarget = false; + + private IListChangeListener targetChangeListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + if (updatingTarget) + return; + IStatus status = cast(IStatus) validationStatus.getValue(); + if (isValid(status)) { + if (stale) { + // this.stale means we are out of sync with target, + // so reset wrapped list to exactly mirror target + stale = false; + updateWrappedList(new ArrayList(target)); + } else { + ListDiff diff = event.diff; + if (computeNextDiff) { + diff = Diffs.computeListDiff(wrappedList, target); + computeNextDiff = false; + } + applyDiff(diff, wrappedList); + fireListChange(diff); + } + } else { + makeStale(); + } + } + }; + + private static bool isValid(IStatus status) { + return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING); + } + + private IStaleListener targetStaleListener = new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + fireStale(); + } + }; + + private IValueChangeListener validationStatusChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + IStatus oldStatus = cast(IStatus) event.diff.getOldValue(); + IStatus newStatus = cast(IStatus) event.diff.getNewValue(); + if (stale && !isValid(oldStatus) && isValid(newStatus)) { + // this.stale means we are out of sync with target, + // reset wrapped list to exactly mirror target + stale = false; + updateWrappedList(new ArrayList(target)); + + // If the validation status becomes valid because of a change in + // target observable + computeNextDiff = true; + } + } + }; + + /** + * @param target + * @param validationStatus + */ + public this(IObservableList target, + IObservableValue validationStatus) { + super(target.getRealm(), new ArrayList(target), target.getElementType()); + Assert.isNotNull(validationStatus, + "Validation status observable cannot be null"); //$NON-NLS-1$ + Assert + .isTrue(target.getRealm().equals(validationStatus.getRealm()), + "Target and validation status observables must be on the same realm"); //$NON-NLS-1$ + this.target = target; + this.validationStatus = validationStatus; + target.addListChangeListener(targetChangeListener); + target.addStaleListener(targetStaleListener); + validationStatus.addValueChangeListener(validationStatusChangeListener); + } + + private void makeStale() { + if (!stale) { + stale = true; + fireStale(); + } + } + + private void updateTargetList(ListDiff diff) { + updatingTarget = true; + try { + if (stale) { + stale = false; + applyDiff(Diffs.computeListDiff(target, wrappedList), target); + } else { + applyDiff(diff, target); + } + } finally { + updatingTarget = false; + } + } + + private void applyDiff(ListDiff diff, List list) { + diff.accept(new class(list) ListDiffVisitor { + List list_; + this(List a){ list_=a;} + public void handleAdd(int index, Object element) { + list_.add(index, element); + } + + public void handleRemove(int index, Object element) { + list_.remove(index); + } + + public void handleReplace(int index, Object oldElement, + Object newElement) { + list_.set(index, newElement); + } + }); + } + + public bool isStale() { + ObservableTracker.getterCalled(this); + return stale || target.isStale(); + } + + public void add(int index, Object element) { + checkRealm(); + wrappedList.add(index, element); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index, + true, element)); + updateTargetList(diff); + fireListChange(diff); + } + + public bool add(Object o) { + checkRealm(); + add(wrappedList.size(), o); + return true; + } + + public bool addAll(Collection c) { + checkRealm(); + return addAll(wrappedList.size(), c); + } + + public bool addAll(int index, Collection c) { + checkRealm(); + Object[] elements = c.toArray(); + ListDiffEntry[] entries = new ListDiffEntry[elements.length]; + for (int i = 0; i < elements.length; i++) { + wrappedList.add(index + i, elements[i]); + entries[i] = Diffs + .createListDiffEntry(index + i, true, elements[i]); + } + ListDiff diff = Diffs.createListDiff(entries); + updateTargetList(diff); + fireListChange(diff); + return true; + } + + public void clear() { + checkRealm(); + if (isEmpty()) + return; + ListDiff diff = Diffs.computeListDiff(wrappedList, + Collections.EMPTY_LIST); + wrappedList.clear(); + updateTargetList(diff); + fireListChange(diff); + } + + public Iterator iterator() { + getterCalled(); + final ListIterator wrappedIterator = wrappedList.listIterator(); + return new class() Iterator { + Object last = null; + + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public Object next() { + return last = wrappedIterator.next(); + } + + public void remove() { + int index = wrappedIterator.previousIndex(); + wrappedIterator.remove(); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry( + index, false, last)); + updateTargetList(diff); + fireListChange(diff); + } + }; + } + + public ListIterator listIterator() { + return listIterator(0); + } + + public ListIterator listIterator(int index) { + getterCalled(); + final ListIterator wrappedIterator = wrappedList.listIterator(index); + return new class() ListIterator { + int lastIndex = -1; + Object last = null; + + public void add(Object o) { + wrappedIterator.add(o); + lastIndex = previousIndex(); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry( + lastIndex, true, o)); + updateTargetList(diff); + fireListChange(diff); + } + + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public bool hasPrevious() { + return wrappedIterator.hasPrevious(); + } + + public Object next() { + last = wrappedIterator.next(); + lastIndex = previousIndex(); + return last; + } + + public int nextIndex() { + return wrappedIterator.nextIndex(); + } + + public Object previous() { + last = wrappedIterator.previous(); + lastIndex = nextIndex(); + return last; + } + + public int previousIndex() { + return wrappedIterator.previousIndex(); + } + + public void remove() { + wrappedIterator.remove(); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry( + lastIndex, false, last)); + lastIndex = -1; + updateTargetList(diff); + fireListChange(diff); + } + + public void set(Object o) { + wrappedIterator.set(o); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry( + lastIndex, false, last), Diffs.createListDiffEntry( + lastIndex, true, o)); + last = o; + updateTargetList(diff); + fireListChange(diff); + } + }; + } + + public Object move(int oldIndex, int newIndex) { + checkRealm(); + int size = wrappedList.size(); + if (oldIndex >= size) + throw new IndexOutOfBoundsException( + "oldIndex: " + oldIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + if (newIndex >= size) + throw new IndexOutOfBoundsException( + "newIndex: " + newIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + if (oldIndex is newIndex) + return wrappedList.get(oldIndex); + Object element = wrappedList.remove(oldIndex); + wrappedList.add(newIndex, element); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry( + oldIndex, false, element), Diffs.createListDiffEntry(newIndex, + true, element)); + updateTargetList(diff); + fireListChange(diff); + return element; + } + + public Object remove(int index) { + checkRealm(); + Object element = wrappedList.remove(index); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index, + false, element)); + updateTargetList(diff); + fireListChange(diff); + return element; + } + + public bool remove(Object o) { + checkRealm(); + int index = wrappedList.indexOf(o); + if (index is -1) + return false; + remove(index); + return true; + } + + public bool removeAll(Collection c) { + checkRealm(); + List list = new ArrayList(wrappedList); + bool changed = list.removeAll(c); + if (changed) { + ListDiff diff = Diffs.computeListDiff(wrappedList, list); + wrappedList = list; + updateTargetList(diff); + fireListChange(diff); + } + return changed; + } + + public bool retainAll(Collection c) { + checkRealm(); + List list = new ArrayList(wrappedList); + bool changed = list.retainAll(c); + if (changed) { + ListDiff diff = Diffs.computeListDiff(wrappedList, list); + wrappedList = list; + updateTargetList(diff); + fireListChange(diff); + } + return changed; + } + + public Object set(int index, Object element) { + checkRealm(); + Object oldElement = wrappedList.set(index, element); + ListDiff diff = Diffs.createListDiff(Diffs.createListDiffEntry(index, + false, oldElement), Diffs.createListDiffEntry(index, true, + element)); + updateTargetList(diff); + fireListChange(diff); + return oldElement; + } + + public synchronized void dispose() { + target.removeListChangeListener(targetChangeListener); + target.removeStaleListener(targetStaleListener); + validationStatus + .removeValueChangeListener(validationStatusChangeListener); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,230 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 218269) + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.ValidatedObservableMap; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.map.IMapChangeListener; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.map.MapChangeEvent; +import org.eclipse.core.databinding.observable.map.MapDiff; +import org.eclipse.core.databinding.observable.map.ObservableMap; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 3.3 + * + */ +public class ValidatedObservableMap : ObservableMap { + private IObservableMap target; + private IObservableValue validationStatus; + + // Only true when out of sync with target due to validation status + private bool stale; + + // True when validation status changes from invalid to valid. + private bool computeNextDiff = false; + + private bool updatingTarget = false; + + private IMapChangeListener targetChangeListener = new class() IMapChangeListener { + public void handleMapChange(MapChangeEvent event) { + if (updatingTarget) + return; + IStatus status = cast(IStatus) validationStatus.getValue(); + if (isValid(status)) { + if (stale) { + // this.stale means we are out of sync with target, + // so reset wrapped list to exactly mirror target + stale = false; + updateWrappedMap(new HashMap(target)); + } else { + MapDiff diff = event.diff; + if (computeNextDiff) { + diff = Diffs.computeMapDiff(wrappedMap, target); + computeNextDiff = false; + } + applyDiff(diff, wrappedMap); + fireMapChange(diff); + } + } else { + makeStale(); + } + } + }; + + private IStaleListener targetStaleListener = new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + fireStale(); + } + }; + + private IValueChangeListener validationStatusChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + IStatus oldStatus = cast(IStatus) event.diff.getOldValue(); + IStatus newStatus = cast(IStatus) event.diff.getNewValue(); + if (stale && !isValid(oldStatus) && isValid(newStatus)) { + // this.stale means we are out of sync with target, + // reset wrapped map to exactly mirror target + stale = false; + updateWrappedMap(new HashMap(target)); + + // If the validation status becomes valid because of a change in + // target observable + computeNextDiff = true; + } + } + }; + + /** + * @param target + * @param validationStatus + */ + public this(IObservableMap target, + IObservableValue validationStatus) { + super(target.getRealm(), new HashMap(target)); + Assert.isNotNull(validationStatus, + "Validation status observable cannot be null"); //$NON-NLS-1$ + Assert + .isTrue(target.getRealm().equals(validationStatus.getRealm()), + "Target and validation status observables must be on the same realm"); //$NON-NLS-1$ + this.target = target; + this.validationStatus = validationStatus; + target.addMapChangeListener(targetChangeListener); + target.addStaleListener(targetStaleListener); + validationStatus.addValueChangeListener(validationStatusChangeListener); + } + + private void updateWrappedMap(Map newMap) { + Map oldMap = wrappedMap; + MapDiff diff = Diffs.computeMapDiff(oldMap, newMap); + wrappedMap = newMap; + fireMapChange(diff); + } + + private static bool isValid(IStatus status) { + return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING); + } + + private void applyDiff(MapDiff diff, Map map) { + for (Iterator iterator = diff.getRemovedKeys().iterator(); iterator + .hasNext();) + map.remove(iterator.next()); + for (Iterator iterator = diff.getChangedKeys().iterator(); iterator + .hasNext();) { + Object key = iterator.next(); + map.put(key, diff.getNewValue(key)); + } + for (Iterator iterator = diff.getAddedKeys().iterator(); iterator + .hasNext();) { + Object key = iterator.next(); + map.put(key, diff.getNewValue(key)); + } + } + + private void makeStale() { + if (!stale) { + stale = true; + fireStale(); + } + } + + private void updateTargetMap(MapDiff diff) { + updatingTarget = true; + try { + if (stale) { + stale = false; + applyDiff(Diffs.computeMapDiff(target, wrappedMap), target); + } else { + applyDiff(diff, target); + } + } finally { + updatingTarget = false; + } + } + + public bool isStale() { + getterCalled(); + return stale || target.isStale(); + } + + public void clear() { + checkRealm(); + if (isEmpty()) + return; + MapDiff diff = Diffs.computeMapDiff(wrappedMap, Collections.EMPTY_MAP); + wrappedMap = new HashMap(); + updateTargetMap(diff); + fireMapChange(diff); + } + + public Object put(Object key, Object value) { + checkRealm(); + MapDiff diff; + Object oldValue; + if (wrappedMap.containsKey(key)) { + oldValue = wrappedMap.put(key, value); + if (wrappedMap.containsKey(key)) { // Changed + diff = Diffs.createMapDiffSingleChange(key, oldValue, value); + } else { // Removed + diff = Diffs.createMapDiffSingleRemove(key, oldValue); + } + } else { // Added + oldValue = wrappedMap.put(key, value); + diff = Diffs.createMapDiffSingleAdd(key, value); + } + updateTargetMap(diff); + fireMapChange(diff); + return oldValue; + } + + public void putAll(Map m) { + checkRealm(); + Map map = new HashMap(wrappedMap); + map.putAll(m); + MapDiff diff = Diffs.computeMapDiff(wrappedMap, map); + wrappedMap = map; + updateTargetMap(diff); + fireMapChange(diff); + } + + public Object remove(Object key) { + checkRealm(); + if (!wrappedMap.containsKey(key)) + return null; + Object oldValue = wrappedMap.remove(key); + MapDiff diff = Diffs.createMapDiffSingleRemove(key, oldValue); + updateTargetMap(diff); + fireMapChange(diff); + return oldValue; + } + + public synchronized void dispose() { + target.removeMapChangeListener(targetChangeListener); + target.removeStaleListener(targetStaleListener); + validationStatus + .removeValueChangeListener(validationStatusChangeListener); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,272 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 218269) + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.ValidatedObservableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.ObservableSet; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; + +/** + * @since 3.3 + * + */ +public class ValidatedObservableSet : ObservableSet { + private IObservableSet target; + private IObservableValue validationStatus; + + // Only true when out of sync with target due to validation status + private bool stale; + + // True when validation status changes from invalid to valid. + private bool computeNextDiff = false; + + private bool updatingTarget = false; + + private ISetChangeListener targetChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + if (updatingTarget) + return; + IStatus status = cast(IStatus) validationStatus.getValue(); + if (isValid(status)) { + if (stale) { + // this.stale means we are out of sync with target, + // so reset wrapped list to exactly mirror target + stale = false; + updateWrappedSet(new HashSet(target)); + } else { + SetDiff diff = event.diff; + if (computeNextDiff) { + diff = Diffs.computeSetDiff(wrappedSet, target); + computeNextDiff = false; + } + applyDiff(diff, wrappedSet); + fireSetChange(diff); + } + } else { + makeStale(); + } + } + }; + + private IStaleListener targetStaleListener = new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + fireStale(); + } + }; + + private IValueChangeListener validationStatusChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + IStatus oldStatus = cast(IStatus) event.diff.getOldValue(); + IStatus newStatus = cast(IStatus) event.diff.getNewValue(); + if (stale && !isValid(oldStatus) && isValid(newStatus)) { + // this.stale means we are out of sync with target, + // reset wrapped set to exactly mirror target + stale = false; + updateWrappedSet(new HashSet(target)); + + // If the validation status becomes valid because of a change in + // target observable + computeNextDiff = true; + } + } + }; + + /** + * @param target + * @param validationStatus + */ + public this(IObservableSet target, + IObservableValue validationStatus) { + super(target.getRealm(), new HashSet(target), target.getElementType()); + Assert.isNotNull(validationStatus, + "Validation status observable cannot be null"); //$NON-NLS-1$ + Assert + .isTrue(target.getRealm().equals(validationStatus.getRealm()), + "Target and validation status observables must be on the same realm"); //$NON-NLS-1$ + this.target = target; + this.validationStatus = validationStatus; + target.addSetChangeListener(targetChangeListener); + target.addStaleListener(targetStaleListener); + validationStatus.addValueChangeListener(validationStatusChangeListener); + } + + private void updateWrappedSet(Set newSet) { + Set oldSet = wrappedSet; + SetDiff diff = Diffs.computeSetDiff(oldSet, newSet); + wrappedSet = newSet; + fireSetChange(diff); + } + + private static bool isValid(IStatus status) { + return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING); + } + + private void applyDiff(SetDiff diff, Set set) { + for (Iterator iterator = diff.getRemovals().iterator(); iterator + .hasNext();) { + set.remove(iterator.next()); + } + for (Iterator iterator = diff.getAdditions().iterator(); iterator + .hasNext();) { + set.add(iterator.next()); + } + } + + private void makeStale() { + if (!stale) { + stale = true; + fireStale(); + } + } + + private void updateTargetSet(SetDiff diff) { + updatingTarget = true; + try { + if (stale) { + stale = false; + applyDiff(Diffs.computeSetDiff(target, wrappedSet), target); + } else { + applyDiff(diff, target); + } + } finally { + updatingTarget = false; + } + } + + public bool isStale() { + getterCalled(); + return stale || target.isStale(); + } + + public bool add(Object o) { + getterCalled(); + bool changed = wrappedSet.add(o); + if (changed) { + SetDiff diff = Diffs.createSetDiff(Collections.singleton(o), + Collections.EMPTY_SET); + updateTargetSet(diff); + fireSetChange(diff); + } + return changed; + } + + public bool addAll(Collection c) { + getterCalled(); + HashSet set = new HashSet(wrappedSet); + bool changed = set.addAll(c); + if (changed) { + SetDiff diff = Diffs.computeSetDiff(wrappedSet, set); + wrappedSet = set; + updateTargetSet(diff); + fireSetChange(diff); + } + return changed; + } + + public void clear() { + getterCalled(); + if (isEmpty()) + return; + SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, wrappedSet); + wrappedSet = new HashSet(); + updateTargetSet(diff); + fireSetChange(diff); + } + + public Iterator iterator() { + getterCalled(); + final Iterator wrappedIterator = wrappedSet.iterator(); + return new class() Iterator { + Object last = null; + + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public Object next() { + return last = wrappedIterator.next(); + } + + public void remove() { + wrappedIterator.remove(); + SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.singleton(last)); + updateTargetSet(diff); + fireSetChange(diff); + } + }; + } + + public bool remove(Object o) { + getterCalled(); + bool changed = wrappedSet.remove(o); + if (changed) { + SetDiff diff = Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.singleton(o)); + updateTargetSet(diff); + fireSetChange(diff); + } + return changed; + } + + public bool removeAll(Collection c) { + getterCalled(); + Set set = new HashSet(wrappedSet); + bool changed = set.removeAll(c); + if (changed) { + SetDiff diff = Diffs.computeSetDiff(wrappedSet, set); + wrappedSet = set; + updateTargetSet(diff); + fireSetChange(diff); + } + return changed; + } + + public bool retainAll(Collection c) { + getterCalled(); + Set set = new HashSet(wrappedSet); + bool changed = set.retainAll(c); + if (changed) { + SetDiff diff = Diffs.computeSetDiff(wrappedSet, set); + wrappedSet = set; + updateTargetSet(diff); + fireSetChange(diff); + } + return changed; + } + + public synchronized void dispose() { + target.removeSetChangeListener(targetChangeListener); + target.removeStaleListener(targetStaleListener); + validationStatus + .removeValueChangeListener(validationStatusChangeListener); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 218269) + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.ValidatedObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.IVetoableValue; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.databinding.observable.value.ValueChangingEvent; +import org.eclipse.core.internal.databinding.Util; +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; + +/** + * An {@link IObservableValue} wrapper that stays in sync with the target + * observable as long as a given validation status is valid. + *
    + *
  • While status is valid, ValidatedObservableValue stays in sync with its + * target. + *
  • When status becomes invalid, ValidatedObservableValue will retain the + * last valid value of its target. + *
  • While status is invalid, changes in the target observable cause + * ValidatedObservableValue to fire a stale event, to indicate that changes are + * pending. + *
  • When status becomes valid, pending value changes are performed (if any) + * and synchronization resumes. + *
+ *

+ * Note: + *

    + *
  • By default, a status is valid if its + * {@link IStatus#getSeverity() severity} is {@link IStatus#OK OK}, + * {@link IStatus#INFO INFO}, or {@link IStatus#WARNING WARNING} + *
  • Calls to {@link #setValuecast(Object)} on the validated observable changes + * the value regardless of the validation status. + *
  • This class will not forward {@link ValueChangingEvent} events from a + * wrapped {@link IVetoableValue}. + *
+ * + * @since 1.2 + */ +public class ValidatedObservableValue : AbstractObservableValue { + private IObservableValue target; + private IObservableValue validationStatus; + + private Object cachedValue; + private bool stale; + private bool updatingTarget = false; + + private IValueChangeListener targetChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + if (updatingTarget) + return; + IStatus status = cast(IStatus) validationStatus.getValue(); + if (isValid(status)) + internalSetValue(event.diff.getNewValue(), false); + else + makeStale(); + } + }; + + private static bool isValid(IStatus status) { + return status.isOK() || status.matches(IStatus.INFO | IStatus.WARNING); + } + + private IStaleListener targetStaleListener = new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + fireStale(); + } + }; + + private IValueChangeListener validationStatusChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + IStatus oldStatus = cast(IStatus) event.diff.getOldValue(); + IStatus newStatus = cast(IStatus) event.diff.getNewValue(); + if (stale && !isValid(oldStatus) && isValid(newStatus)) { + internalSetValue(target.getValue(), false); + } + } + }; + + /** + * Constructs an observable value + * + * @param target + * the observable value to be wrapped + * @param validationStatus + * an observable value of type {@link IStatus}.classinfo which + * contains the current validation status + */ + public this(IObservableValue target, + IObservableValue validationStatus) { + super(target.getRealm()); + Assert.isNotNull(validationStatus, + "Validation status observable cannot be null"); //$NON-NLS-1$ + Assert + .isTrue(target.getRealm().equals(validationStatus.getRealm()), + "Target and validation status observables must be on the same realm"); //$NON-NLS-1$ + this.target = target; + this.validationStatus = validationStatus; + this.cachedValue = target.getValue(); + + target.addValueChangeListener(targetChangeListener); + target.addStaleListener(targetStaleListener); + validationStatus.addValueChangeListener(validationStatusChangeListener); + } + + private void makeStale() { + if (!stale) { + stale = true; + fireStale(); + } + } + + public bool isStale() { + ObservableTracker.getterCalled(this); + return stale || target.isStale(); + } + + protected Object doGetValue() { + return cachedValue; + } + + private void internalSetValue(Object value, bool updateTarget) { + Object oldValue = cachedValue; + cachedValue = value; + if (updateTarget) { + updatingTarget = true; + try { + target.setValue(value); + cachedValue = target.getValue(); + } finally { + updatingTarget = false; + } + } + stale = false; + if (!Util.equals(oldValue, cachedValue)) + fireValueChange(Diffs.createValueDiff(oldValue, cachedValue)); + } + + protected void doSetValue(Object value) { + internalSetValue(value, true); + } + + public Object getValueType() { + return target.getValueType(); + } + + public synchronized void dispose() { + target.removeValueChangeListener(targetChangeListener); + target.removeStaleListener(targetStaleListener); + validationStatus + .removeValueChangeListener(validationStatusChangeListener); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,177 @@ +/******************************************************************************* + * Copyright (c) 2005-2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 147515 + * Matthew Hall - bug 221351 + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableList; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IObserving; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ObservableList; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.runtime.Assert; + +/** + * @since 3.2 + * + */ + +public class DetailObservableList : ObservableList , IObserving { + + private bool updating = false; + + private IListChangeListener innerChangeListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + if (!updating) { + fireListChange(event.diff); + } + } + }; + + private Object currentOuterValue; + + private IObservableList innerObservableList; + + private IObservableFactory factory; + + private IObservableValue outerObservableValue; + + private Object detailType; + + /** + * @param factory + * @param outerObservableValue + * @param detailType + */ + public this(IObservableFactory factory, + IObservableValue outerObservableValue, Object detailType) { + super(outerObservableValue.getRealm(), Collections.EMPTY_LIST, detailType); + this.factory = factory; + this.outerObservableValue = outerObservableValue; + this.detailType = detailType; + updateInnerObservableList(outerObservableValue); + + outerObservableValue.addValueChangeListener(outerChangeListener); + } + + IValueChangeListener outerChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + List oldList = new ArrayList(wrappedList); + updateInnerObservableList(outerObservableValue); + fireListChange(Diffs.computeListDiff(oldList, wrappedList)); + } + }; + + private void updateInnerObservableList(IObservableValue outerObservableValue) { + if (innerObservableList !is null) { + innerObservableList.removeListChangeListener(innerChangeListener); + innerObservableList.dispose(); + } + currentOuterValue = outerObservableValue.getValue(); + if (currentOuterValue is null) { + innerObservableList = null; + wrappedList = Collections.EMPTY_LIST; + } else { + this.innerObservableList = cast(IObservableList) factory + .createObservable(currentOuterValue); + wrappedList = innerObservableList; + + if (detailType !is null) { + Object innerValueType = innerObservableList.getElementType(); + Assert.isTrue(getElementType().equals(innerValueType), + "Cannot change value type in a nested observable list"); //$NON-NLS-1$ + } + innerObservableList.addListChangeListener(innerChangeListener); + } + } + + public bool add(Object o) { + return wrappedList.add(o); + } + + public void add(int index, Object element) { + wrappedList.add(index, element); + } + + public bool remove(Object o) { + return wrappedList.remove(o); + } + + public Object set(int index, Object element) { + return wrappedList.set(index, element); + } + + public Object move(int oldIndex, int newIndex) { + if (innerObservableList !is null) + return innerObservableList.move(oldIndex, newIndex); + return super.move(oldIndex, newIndex); + } + + public Object remove(int index) { + return wrappedList.remove(index); + } + + public bool addAll(Collection c) { + return wrappedList.addAll(c); + } + + public bool addAll(int index, Collection c) { + return wrappedList.addAll(index, c); + } + + public bool removeAll(Collection c) { + return wrappedList.removeAll(c); + } + + public bool retainAll(Collection c) { + return wrappedList.retainAll(c); + } + + public void clear() { + wrappedList.clear(); + } + + public void dispose() { + super.dispose(); + + if (outerObservableValue !is null) { + outerObservableValue.removeValueChangeListener(outerChangeListener); + outerObservableValue.dispose(); + } + if (innerObservableList !is null) { + innerObservableList.removeListChangeListener(innerChangeListener); + innerObservableList.dispose(); + } + currentOuterValue = null; + factory = null; + innerObservableList = null; + innerChangeListener = null; + } + + public Object getObserved() { + if ( null !is cast(IObserving)innerObservableList ) { + return (cast(IObserving) innerObservableList).getObserved(); + } + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,128 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 221704) + * Matthew Hall - bug 223114 + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableMap; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.map.IMapChangeListener; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.map.MapChangeEvent; +import org.eclipse.core.databinding.observable.map.ObservableMap; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; + +/** + * @since 1.1 + * + */ +public class DetailObservableMap : ObservableMap { + private bool updating = false; + + private IObservableValue master; + private IObservableFactory detailFactory; + + private IObservableMap detailMap; + + private IValueChangeListener masterChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + Map oldMap = new HashMap(wrappedMap); + updateDetailMap(); + fireMapChange(Diffs.computeMapDiff(oldMap, wrappedMap)); + } + }; + + private IMapChangeListener detailChangeListener = new class() IMapChangeListener { + public void handleMapChange(MapChangeEvent event) { + if (!updating) { + fireMapChange(event.diff); + } + } + }; + + /** + * Constructs a new DetailObservableMap + * + * @param detailFactory + * observable factory that creates IObservableMap instances given + * the current value of master observable value + * @param master + * + */ + public this(IObservableFactory detailFactory, + IObservableValue master) { + super(master.getRealm(), Collections.EMPTY_MAP); + this.master = master; + this.detailFactory = detailFactory; + + updateDetailMap(); + master.addValueChangeListener(masterChangeListener); + } + + private void updateDetailMap() { + Object masterValue = master.getValue(); + if (detailMap !is null) { + detailMap.removeMapChangeListener(detailChangeListener); + detailMap.dispose(); + } + + if (masterValue is null) { + detailMap = null; + wrappedMap = Collections.EMPTY_MAP; + } else { + detailMap = cast(IObservableMap) detailFactory + .createObservable(masterValue); + wrappedMap = detailMap; + detailMap.addMapChangeListener(detailChangeListener); + } + } + + public Object put(Object key, Object value) { + return detailMap.put(key, value); + } + + public void putAll(Map map) { + detailMap.putAll(map); + } + + public Object remove(Object key) { + return detailMap.remove(key); + } + + public void clear() { + detailMap.clear(); + } + + public synchronized void dispose() { + if (master !is null) { + master.removeValueChangeListener(masterChangeListener); + master = null; + masterChangeListener = null; + } + detailFactory = null; + if (detailMap !is null) { + detailMap.removeMapChangeListener(detailChangeListener); + detailMap.dispose(); + detailMap = null; + } + detailChangeListener = null; + super.dispose(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2005-2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 221351 + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IObserving; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.ObservableSet; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.runtime.Assert; + +/** + * @since 3.2 + * + */ +public class DetailObservableSet : ObservableSet , IObserving { + + private bool updating = false; + + private ISetChangeListener innerChangeListener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + if (!updating) { + fireSetChange(event.diff); + } + } + }; + + private Object currentOuterValue; + + private IObservableSet innerObservableSet; + + private IObservableValue outerObservableValue; + + private IObservableFactory factory; + + /** + * @param factory + * @param outerObservableValue + * @param detailType + */ + public this(IObservableFactory factory, + IObservableValue outerObservableValue, Object detailType) { + super(outerObservableValue.getRealm(), Collections.EMPTY_SET, + detailType); + this.factory = factory; + this.outerObservableValue = outerObservableValue; + updateInnerObservableSet(outerObservableValue); + + outerObservableValue.addValueChangeListener(outerChangeListener); + } + + IValueChangeListener outerChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + Set oldSet = new HashSet(wrappedSet); + updateInnerObservableSet(outerObservableValue); + fireSetChange(Diffs.computeSetDiff(oldSet, wrappedSet)); + } + }; + + private void updateInnerObservableSet(IObservableValue outerObservableValue) { + currentOuterValue = outerObservableValue.getValue(); + if (innerObservableSet !is null) { + innerObservableSet.removeSetChangeListener(innerChangeListener); + innerObservableSet.dispose(); + } + if (currentOuterValue is null) { + innerObservableSet = null; + wrappedSet = Collections.EMPTY_SET; + } else { + this.innerObservableSet = cast(IObservableSet) factory + .createObservable(currentOuterValue); + wrappedSet = innerObservableSet; + + if (elementType !is null) { + Object innerValueType = innerObservableSet.getElementType(); + + Assert.isTrue(elementType.equals(innerValueType), + "Cannot change value type in a nested observable set"); //$NON-NLS-1$ + } + + innerObservableSet.addSetChangeListener(innerChangeListener); + } + } + + public bool add(Object o) { + getterCalled(); + return wrappedSet.add(o); + } + + public bool remove(Object o) { + getterCalled(); + return wrappedSet.remove(o); + } + + public bool addAll(Collection c) { + getterCalled(); + return wrappedSet.addAll(c); + } + + public bool removeAll(Collection c) { + getterCalled(); + return wrappedSet.removeAll(c); + } + + public bool retainAll(Collection c) { + getterCalled(); + return wrappedSet.retainAll(c); + } + + public void clear() { + getterCalled(); + wrappedSet.clear(); + } + + public void dispose() { + super.dispose(); + + if (outerObservableValue !is null) { + outerObservableValue.removeValueChangeListener(outerChangeListener); + outerObservableValue.dispose(); + } + if (innerObservableSet !is null) { + innerObservableSet.removeSetChangeListener(innerChangeListener); + innerObservableSet.dispose(); + } + currentOuterValue = null; + factory = null; + innerObservableSet = null; + innerChangeListener = null; + } + + public Object getObserved() { + if ( null !is cast(IObserving)innerObservableSet ) { + return (cast(IObserving) innerObservableSet).getObserved(); + } + return null; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Brad Reynolds - bug 147515 + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.masterdetail.DetailObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IObserving; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.runtime.Assert; + +/** + * @since 1.0 + * + */ +public class DetailObservableValue : AbstractObservableValue , IObserving { + + private bool updating = false; + + private IValueChangeListener innerChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + if (!updating) { + fireValueChange(event.diff); + } + } + }; + + private Object currentOuterValue; + + private IObservableValue innerObservableValue; + + private Object detailType; + + private IObservableValue outerObservableValue; + + private IObservableFactory factory; + + /** + * @param outerObservableValue + * @param factory + * @param detailType + */ + public this(IObservableValue outerObservableValue, + IObservableFactory factory, Object detailType) { + super(outerObservableValue.getRealm()); + this.factory = factory; + this.detailType = detailType; + this.outerObservableValue = outerObservableValue; + updateInnerObservableValue(outerObservableValue); + + outerObservableValue.addValueChangeListener(outerChangeListener); + } + + IValueChangeListener outerChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + Object oldValue = doGetValue(); + updateInnerObservableValue(outerObservableValue); + fireValueChange(Diffs.createValueDiff(oldValue, doGetValue())); + } + }; + + private void updateInnerObservableValue( + IObservableValue outerObservableValue) { + currentOuterValue = outerObservableValue.getValue(); + if (innerObservableValue !is null) { + innerObservableValue.removeValueChangeListener(innerChangeListener); + innerObservableValue.dispose(); + } + if (currentOuterValue is null) { + innerObservableValue = null; + } else { + this.innerObservableValue = cast(IObservableValue) factory + .createObservable(currentOuterValue); + Object innerValueType = innerObservableValue.getValueType(); + + if (detailType !is null) { + Assert + .isTrue( + detailType.equals(innerValueType), + "Cannot change value type in a nested observable value, from " + innerValueType + " to " + detailType); //$NON-NLS-1$ //$NON-NLS-2$ + } + innerObservableValue.addValueChangeListener(innerChangeListener); + } + } + + public void doSetValue(Object value) { + if (innerObservableValue !is null) + innerObservableValue.setValue(value); + } + + public Object doGetValue() { + return innerObservableValue is null ? null : innerObservableValue + .getValue(); + } + + public Object getValueType() { + return detailType; + } + + public void dispose() { + super.dispose(); + + if (outerObservableValue !is null) { + outerObservableValue.removeValueChangeListener(outerChangeListener); + outerObservableValue.dispose(); + } + if (innerObservableValue !is null) { + innerObservableValue.removeValueChangeListener(innerChangeListener); + innerObservableValue.dispose(); + } + currentOuterValue = null; + factory = null; + innerObservableValue = null; + innerChangeListener = null; + } + + public Object getObserved() { + if ( null !is cast(IObserving)innerObservableValue ) { + return (cast(IObserving)innerObservableValue).getObserved(); + } + return null; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/AbstractObservableTree.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/AbstractObservableTree.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164134 + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.AbstractObservableTree; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.AbstractObservable; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.Status; + +/** + * @since 3.3 + * + */ +public abstract class AbstractObservableTree : AbstractObservable + , IObservableTree { + + private bool stale; + + private ListenerList treeListeners = new ListenerListcast(ListenerList.IDENTITY); + + /** + * @param realm + */ + public this(Realm realm) { + super(realm); + } + + public void addChild(TreePath parentPath, Object childElement) { + throw new UnsupportedOperationException(); + } + + public void addTreeChangeListener(ITreeChangeListener listener) { + treeListeners.add(listener); + } + + public int getChildCount(TreePath parentPath) { + return getChildren(parentPath).length; + } + + public bool hasChildren(TreePath parentPath) { + return getChildCount(parentPath) > 0; + } + + public void insertChild(TreePath parentPath, int index, Object childElement) { + throw new UnsupportedOperationException(); + } + + public bool isLazy() { + return false; + } + + public bool isOrdered() { + return false; + } + + public void removeChild(TreePath parentPath, Object childElement) { + throw new UnsupportedOperationException(); + } + + public void removeChild(TreePath parentPath, int index) { + throw new UnsupportedOperationException(); + } + + public void removeTreeChangeListener(ITreeChangeListener listener) { + treeListeners.remove(listener); + } + + public void setChildCount(TreePath parentPath, int count) { + throw new UnsupportedOperationException(); + } + + public void setChildren(TreePath parentPath, Object[] children) { + throw new UnsupportedOperationException(); + } + + public void updateChildren(IChildrenUpdate update) { + TreePath parent = update.getParent(); + Object[] children = getChildren(parent); + for (int i = 0; i < update.getLength(); i++) { + int targetIndex = update.getOffset() + i; + if (targetIndex < children.length) { + update.setChild(children[targetIndex], targetIndex); + } else { + update + .setStatus(new Status( + IStatus.WARNING, + Policy.JFACE_DATABINDING, + IStatus.OK, + BindingMessages + .getStringcast(BindingMessages.INDEX_OUT_OF_RANGE), + null)); + } + } + update.done(); + } + + public void updateChildrenCount(IChildrenCountUpdate update) { + TreePath[] parents = update.getParents(); + for (int i = 0; i < parents.length; i++) { + update.setChildCount(parents[i], getChildCount(parents[i])); + } + update.done(); + } + + public void updateHasChildren(IHasChildrenUpdate update) { + TreePath[] parents = update.getElements(); + for (int i = 0; i < parents.length; i++) { + update.setHasChilren(parents[i], hasChildren(parents[i])); + } + update.done(); + } + + public bool isStale() { + return stale; + } + + /** + * @param stale + */ + public void setStale(bool stale) { + this.stale = stale; + if (stale) { + fireStale(); + } + } + + protected void fireTreeChange(TreeDiff diff) { + // fire general change event first + fireChange(); + + Object[] listeners = treeListeners.getListeners(); + TreeChangeEvent event = new TreeChangeEvent(this, diff); + for (int i = 0; i < listeners.length; i++) { + (cast(ITreeChangeListener) listeners[i]).handleTreeChange(event); + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IChildrenCountUpdate.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IChildrenCountUpdate.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.tree.IChildrenCountUpdate; + +import java.lang.all; + +/** + * Request monitor used to collect the number of children for an element in a lazy + * observable tree. + * + * @since 3.3 + */ +public interface IChildrenCountUpdate : IViewerUpdate { + + /** + * Returns the parent elements that children counts have been requested for + * as tree paths. An empty path identifies the root element. + * + * @return parent elements as tree paths + */ + public TreePath[] getParents(); + + /** + * Sets the number of children for the given parent. + * + * @param parentPath + * parent element or empty path for root element + * @param numChildren + * number of children + */ + public void setChildCount(TreePath parentPath, int numChildren); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IChildrenUpdate.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IChildrenUpdate.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.tree.IChildrenUpdate; + +import java.lang.all; + +/** + * Context sensitive children update request for a parent and subrange of its + * children. + * + * @since 3.3 + */ +public interface IChildrenUpdate : IViewerUpdate { + + /** + * Returns the parent element that children are being requested for + * as a tree path. An empty path identifies the root element. + * + * @return parent element as a tree path + */ + public TreePath getParent(); + + /** + * Returns the offset at which children have been requested for. This is + * the index of the first child being requested. + * + * @return offset at which children have been requested for + */ + public int getOffset(); + + /** + * Returns the number of children requested. + * + * @return number of children requested + */ + public int getLength(); + + /** + * Sets the child for this request's parent at the given offset. + * + * @param child child + * @param index child offset + * + * TODO: what to do with null + */ + public void setChild(Object child, int index); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IHasChildrenUpdate.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IHasChildrenUpdate.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.tree.IHasChildrenUpdate; + +import java.lang.all; + +/** + * Context sensitive update request for whether elements have children. + * + * @since 3.3 + */ +public interface IHasChildrenUpdate : IViewerUpdate { + + /** + * The elements this request is for specified as tree paths. An empty path + * identifies the root element. + * + * @return elements as tree paths + */ + public TreePath[] getElements(); + + /** + * Sets whether the given element has children. + * + * @param element + * tree path to element, or empty for root element + * @param hasChildren + * whether it has children + */ + public void setHasChilren(TreePath element, bool hasChildren); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IObservableTree.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IObservableTree.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.IObservableTree; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservable; + +/** + * + * A tree whose changes can be tracked by tree change listeners. If the tree is + * ordered ({@link #isOrdered()}), the order of children for a given tree path + * matters, and tree change notifications will always specify indices. If the + * tree is unordered, the children of a tree path are an unordered set and + * indices in change notifications are not specified. + * + *

+ * This interface is not intended to be implemented by clients. Clients should + * instead subclass one of the framework classes that implement this interface. + * Note that direct implementers of this interface outside of the framework will + * be broken in future releases when methods are added to this interface. + *

+ * + * @since 1.1 + */ +public interface IObservableTree : IObservable { + + /** + * Element that can be returned from synchronous getters if this observable + * tree is lazy. + */ + public final static Object UNKNOWN_ELEMENT = new Object(); + + /** + * @param listener + */ + public void addTreeChangeListener(ITreeChangeListener listener); + + /** + * @param listener + */ + public void removeTreeChangeListener(ITreeChangeListener listener); + + /** + * Returns whether the order of children for a given parent is important. If + * this tree is ordered, tree change notifications will always specify + * indices. + * + * @return true if the order of children for a given parent is important + */ + public bool isOrdered(); + + /** + * Returns whether this tree is optimized to fetch subsets of children + * lazily and possibly asynchronously. Implies {@link #isOrdered()}. + * + * @return true if this tree + */ + public bool isLazy(); + + /** + * @param parentPath + * @return the children at the given parent path + */ + public Object[] getChildren(TreePath parentPath); + + /** + * @param parentPath + * @param children + */ + public void setChildren(TreePath parentPath, Object[] children); + + /** + * @param parentPath + * @param childElement + */ + public void addChild(TreePath parentPath, Object childElement); + + /** + * @param parentPath + * @param childElement + */ + public void removeChild(TreePath parentPath, Object childElement); + + /** + * @param parentPath + * @param index + * @param childElement + */ + public void insertChild(TreePath parentPath, int index, Object childElement); + + /** + * @param parentPath + * @param index + */ + public void removeChild(TreePath parentPath, int index); + + /** + * @param parentPath + * @return true if the element at the given path has children + */ + public bool hasChildren(TreePath parentPath); + + /** + * @param parentPath + * @return the number of children of the element at the given path + */ + public int getChildCount(TreePath parentPath); + + /** + * @param parentPath + * @param count + */ + public void setChildCount(TreePath parentPath, int count); + + /** + * Updates the number of children for the given parent elements in the + * specified request. + * + * @param update specifies counts to update and stores result + */ + public void updateChildrenCount(IChildrenCountUpdate update); + + /** + * Updates children as requested by the update. + * + * @param update specifies children to update and stores result + */ + public void updateChildren(IChildrenUpdate update); + + /** + * Updates whether elements have children. + * + * @param update specifies elements to update and stores result + */ + public void updateHasChildren(IHasChildrenUpdate update); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IOrderedTreeProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IOrderedTreeProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.IOrderedTreeProvider; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.IObservableList; + +/** + * Objects that implement this interface are capable of describing a tree by + * returning the list of children of any given element in the tree. + * + * @since 3.3 + */ +public interface IOrderedTreeProvider { + /** + * Returns the children of the given element, or null if the element is a + * leaf node. The caller of this method is expected to dispose the result + * list when it is no longer needed. + * + * @param element + * the tree path of the element to query + * @return the children of the given element, or null if the element is a + * leaf node + */ + IObservableList createChildList(TreePath element); + + /** + * @return the realm shared by all child lists + */ + Realm getRealm(); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/ITreeChangeListener.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/ITreeChangeListener.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.ITreeChangeListener; + +import java.lang.all; + +/** + * @since 3.3 + * + */ +public interface ITreeChangeListener { + /** + * @param event + */ + void handleTreeChange(TreeChangeEvent event); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IUnorderedTreeProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IUnorderedTreeProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.set.IObservableSet; + +/** + * @since 1.0 + * + */ +public interface IUnorderedTreeProvider { + /** + * @return the realm for the createChildSet method + */ + public Realm getRealm(); + + /** + * Returns the children of the given element, or null if the element is a leaf node. + * The caller of this method is expected to dispose the result set when it is no + * longer needed. + * + * @param element element to query + * @return the children of the given element, or null if the element is a leaf node + */ + IObservableSet createChildSet(Object element); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IViewerUpdate.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IViewerUpdate.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.tree.IViewerUpdate; + +import java.lang.all; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; + +/** + * A context sensitive viewer update request. + * + * @since 3.3 + */ +public interface IViewerUpdate : IProgressMonitor { + + /** + * Sets the status of this request, possibly null. + * When a request fails, the status indicates why the request failed. + * A null status is considered to be successful. + * + * @param status request status + */ + public void setStatus(IStatus status); + + /** + * Returns the status of this request, or null. + * + * @return request status or null + */ + public IStatus getStatus(); + + /** + * Returns the model element corresponding to the given tree path. + * Returns the root element for the empty path. + * + * @param path viewer tree path + * @return corresponding model element + */ + public Object getElement(TreePath path); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeChangeEvent.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeChangeEvent.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.TreeChangeEvent; + +import java.lang.all; + +import java.util.EventObject; + +/** + * @since 3.3 + * + */ +public class TreeChangeEvent : EventObject { + + /** + * + */ + private static final long serialVersionUID = -3198503763995528027L; + /** + * + */ + public TreeDiff diff; + + /** + * @param source + * @param diff + */ + public this(IObservableTree source, TreeDiff diff) { + super(source); + this.diff = diff; + } + + /** + * @return the observable tree from which this event originated + */ + public IObservableTree getObservable() { + return cast(IObservableTree) getSource(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiff.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiff.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.TreeDiff; + +import java.lang.all; + +/** + * Describes the difference between two trees as a tree of tree diff nodes. + * + * @since 1.1 + * + */ +public abstract class TreeDiff : TreeDiffNode { + + /** + * Returns the tree path (possibly empty) of the parent, or + * null if the underlying tree is not lazy and never contains + * duplicate elements. + * + * @return the tree path (possibly empty) of the unchanged parent, or + * null + */ + public abstract TreePath getParentPath(); + + /** + * @param visitor + */ + public void accept(TreeDiffVisitor visitor) { + doAccept(visitor, getParentPath()); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiffNode.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiffNode.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.TreeDiffNode; + +import java.lang.all; + +/** + * @since 1.1 + * + */ +public abstract class TreeDiffNode { + + /** + * + */ + public final static int NO_CHANGE = 0x00; + + /** + * + */ + public final static int ADDED = 0x01; + + /** + * + */ + public final static int REMOVED = 0x02; + + /** + * + */ + public final static int REPLACED = 0x03; + + /** + * + */ + public static final TreeDiffNode[] NO_CHILDREN = new TreeDiffNode[0]; + + /** + * + */ + public static final int INDEX_UNKNOWN = -1; + + /** + * @return the change type + */ + public abstract int getChangeType(); + + /** + * @return the element that was removed, or the replaced element + */ + public abstract Object getOldElement(); + + /** + * @return the element that was not changed, added, or the replacement + * element + */ + public abstract Object getNewElement(); + + /** + * @return the index at which the element was added, removed, or replaced + */ + public abstract int getIndex(); + + /** + * Returns the child tree diff objects that describe changes to children. If + * the change type is REMOVED, there will be no children. + * + * @return the nodes representing changes to children + */ + public abstract TreeDiffNode[] getChildren(); + + protected void doAccept(TreeDiffVisitor visitor, TreePath parentPath) { + TreePath currentPath = parentPath.createChildPath(getNewElement()); + bool recurse = visitor.visit(this, currentPath); + if (recurse) { + TreeDiffNode[] children = getChildren(); + for (int i = 0; i < children.length; i++) { + TreeDiffNode child = children[i]; + child.doAccept(visitor, currentPath); + } + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiffVisitor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiffVisitor.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.observable.tree.TreeDiffVisitor; + +import java.lang.all; + +/** + * @since 3.3 + * + */ +public abstract class TreeDiffVisitor { + + /** + * Visits the given tree diff. + * + * @param diff + * the diff to visit + * @param currentPath + * the current path (the diff's element is the last segment of + * the path) + * + * @return true if the tree diff's children should be + * visited; false if they should be skipped. + */ + public abstract bool visit(TreeDiffNode diff, TreePath currentPath); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreePath.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreePath.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 118516 + *******************************************************************************/ +module org.eclipse.core.internal.databinding.observable.tree.TreePath; + +import java.lang.all; + +import org.eclipse.core.runtime.Assert; + +/** + * A tree path denotes a model element in a tree viewer. Tree path objects have + * value semantics. A model element is represented by a path of elements in the + * tree from the root element to the leaf element. + *

+ * Clients may instantiate this class. Not intended to be subclassed. + *

+ * + * @since 3.2 + */ +public final class TreePath { + + /** + * Constant for representing an empty tree path. + */ + public static final TreePath EMPTY = new TreePath(new Object[0]); + + private Object[] segments; + + private int hash; + + /** + * Constructs a path identifying a leaf node in a tree. + * + * @param segments + * path of elements to a leaf node in a tree, starting with the + * root element + */ + public this(Object[] segments) { + Assert.isNotNull(segments, "Segments array cannot be null"); //$NON-NLS-1$ + this.segments = new Object[segments.length]; + for (int i = 0; i < segments.length; i++) { + Assert.isNotNull(segments[i], "Segments array cannot contain null"); //$NON-NLS-1$ + this.segments[i] = segments[i]; + } + } + + /** + * Returns the element at the specified index in this path. + * + * @param index + * index of element to return + * @return element at the specified index + */ + public Object getSegment(int index) { + return segments[index]; + } + + /** + * Returns the number of elements in this path. + * + * @return the number of elements in this path + */ + public int getSegmentCount() { + return segments.length; + } + + /** + * Returns the first element in this path. + * + * @return the first element in this path + */ + public Object getFirstSegment() { + if (segments.length is 0) { + return null; + } + return segments[0]; + } + + /** + * Returns the last element in this path. + * + * @return the last element in this path + */ + public Object getLastSegment() { + if (segments.length is 0) { + return null; + } + return segments[segments.length - 1]; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public override bool opEquals(Object other) { + if (!( null !is cast(TreePath)other )) { + return false; + } + TreePath otherPath = cast(TreePath) other; + if (segments.length !is otherPath.segments.length) { + return false; + } + for (int i = 0; i < segments.length; i++) { + if (!segments[i].equals(otherPath.segments[i])) { + return false; + } + } + return true; + } + + public int hashCode() { + if (hash is 0) { + for (int i = 0; i < segments.length; i++) { + hash += segments[i].hashCode(); + } + } + return hash; + } + + /** + * Returns whether this path starts with the same segments as the given + * path, using the given comparer to compare segments. + * + * @param treePath + * path to compare to + * @return whether the given path is a prefix of this path, or the same as + * this path + */ + public bool startsWith(TreePath treePath) { + int thisSegmentCount = getSegmentCount(); + int otherSegmentCount = treePath.getSegmentCount(); + if (otherSegmentCount is thisSegmentCount) { + return equals(treePath); + } + if (otherSegmentCount > thisSegmentCount) { + return false; + } + for (int i = 0; i < otherSegmentCount; i++) { + Object otherSegment = treePath.getSegment(i); + if (!otherSegment.equals(segments[i])) { + return false; + } + } + return true; + } + + /** + * Returns a copy of this tree path with one segment removed from the end, + * or null if this tree path has no segments. + * @return a tree path + */ + public TreePath getParentPath() { + int segmentCount = getSegmentCount(); + if (segmentCount <= 1) { + return null; + } + Object[] parentSegments = new Object[segmentCount - 1]; + System.arraycopy(segments, 0, parentSegments, 0, segmentCount - 1); + return new TreePath(parentSegments); + } + + /** + * Returns a copy of this tree path with the given segment added at the end. + * @param newSegment + * @return a tree path + */ + public TreePath createChildPath(Object newSegment) { + int segmentCount = getSegmentCount(); + Object[] childSegments = new Object[segmentCount + 1]; + if(segmentCount>0) { + System.arraycopy(segments, 0, childSegments, 0, segmentCount); + } + childSegments[segmentCount] = newSegment; + return new TreePath(childSegments); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +To be written. +

+Package Specification

+

+This package provides classes that can be used to ...

+ + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator; + +import java.lang.all; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser.ParseResult; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Validates a number that is to be converted by a {@link NumberFormatConverter}. + * Validation is comprised of parsing the String and range checks. + * + * @since 1.0 + */ +public abstract class AbstractStringToNumberValidator : IValidator { + private final NumberFormatConverter converter; + private final bool toPrimitive; + + private final Number min; + private final Number max; + + private String outOfRangeMessage; + + /** + * Constructs a new instance. + * + * @param converter converter and thus formatter to be used in validation + * @param min minimum value, used for reporting a range error to the user + * @param max maximum value, used for reporting a range error to the user + */ + protected this(NumberFormatConverter converter, + Number min, Number max) { + this.converter = converter; + this.min = min; + this.max = max; + + if (null !is cast(ClassInfo)converter.getToType()) { + ClassInfo clazz = cast(ClassInfo) converter.getToType(); + toPrimitive = clazz.isPrimitive(); + } else { + toPrimitive = false; + } + } + + /** + * Validates the provided value. An error status is returned if: + *
    + *
  • The value cannot be parsed.
  • + *
  • The value is out of range.
  • + *
+ * + * @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object) + */ + public final IStatus validate(Object value) { + ParseResult result = StringToNumberParser.parse(value, converter + .getNumberFormat(), toPrimitive); + + if (result.getNumber() !is null) { + if (!isInRange(result.getNumber())) { + if (outOfRangeMessage is null) { + outOfRangeMessage = StringToNumberParser + .createOutOfRangeMessage(min, max, converter + .getNumberFormat()); + } + + return ValidationStatus.error(outOfRangeMessage); + } + } else if (result.getPosition() !is null) { + String parseErrorMessage = StringToNumberParser.createParseErrorMessage( + cast(String) value, result.getPosition()); + + return ValidationStatus.error(parseErrorMessage); + } + + return Status.OK_STATUS; + } + + /** + * Invoked by {@link #validatecast(Object)} when the range is to be validated. + * + * @param number + * @return true if in range + */ + protected abstract bool isInRange(Number number); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberFormatConverter.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberFormatConverter.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberFormatConverter; + +import java.lang.all; + +import org.eclipse.core.databinding.conversion.Converter; + +import com.ibm.icu.text.NumberFormat; + +/** + * Converter that uses a number format for conversion. + * + * @since 1.0 + */ +public abstract class NumberFormatConverter : Converter { + private final NumberFormat numberFormat; + + /** + * @param fromType + * @param toType + * @param numberFormat + */ + public this(Object fromType, Object toType, NumberFormat numberFormat) { + super(fromType, toType); + + this.numberFormat = numberFormat; + } + + /** + * @return number format + */ + /*package */ NumberFormat getNumberFormat() { + return numberFormat; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToByteValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToByteValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToByteValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates if a Number can fit in a Byte. + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public class NumberToByteValidator : NumberToNumberValidator { + private static final Byte MAX = new Bytecast(Byte.MAX_VALUE); + private static final Byte MIN = new Bytecast(Byte.MIN_VALUE); + + /** + * @param converter + */ + public this(NumberToByteConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number) + */ + protected bool inRange(Number number) { + return StringToNumberParser.inByteRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToDoubleValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToDoubleValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToDoubleValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates if a Number can fit in a Double. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToDoubleValidator : NumberToNumberValidator { + private static final Double MIN = new Doublecast(Double.MIN_VALUE); + private static final Double MAX = new Doublecast(Double.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberToDoubleConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number) + */ + protected bool inRange(Number number) { + return StringToNumberParser.inDoubleRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToFloatValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToFloatValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToFloatValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates if a Number can fit in a Float. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToFloatValidator : NumberToNumberValidator { + private static final Float MIN = new Floatcast(Float.MIN_VALUE); + private static final Float MAX = new Floatcast(Float.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberToFloatConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number) + */ + protected bool inRange(Number number) { + return StringToNumberParser.inFloatRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToIntegerValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToIntegerValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToIntegerValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates if a Number can fit in a Integer. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToIntegerValidator : NumberToNumberValidator { + private static final Integer MIN = new Integercast(Integer.MIN_VALUE); + private static final Integer MAX = new Integercast(Integer.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberToIntegerConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number) + */ + protected bool inRange(Number number) { + return StringToNumberParser.inIntegerRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToLongValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToLongValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToLongValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates if a Number can fit in a Long. + *

+ * Class is thread safe. + *

+ * @since 1.0 + */ +public class NumberToLongValidator : NumberToNumberValidator { + private static final Long MIN = new Longcast(Long.MIN_VALUE); + private static final Long MAX = new Longcast(Long.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberToLongConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number) + */ + protected bool inRange(Number number) { + return StringToNumberParser.inLongRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToNumberValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToNumberValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToNumberValidator; + +import java.lang.all; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Base class for validators that validate if a Number can fit in another Number type. + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public abstract class NumberToNumberValidator : IValidator { + private final NumberToNumberConverter converter; + + private final Number min; + + private final Number max; + + private String outOfRangeMessage; + + private final bool primitive; + + /** + * @param converter + * @param min + * can be null + * @param max + * can be null + */ + protected this(NumberToNumberConverter converter, + Number min, Number max) { + this.converter = converter; + this.min = min; + this.max = max; + + primitive = (cast(ClassInfo) converter.getToType()).isPrimitive(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object) + */ + public final IStatus validate(Object value) { + if (value is null) { + if (primitive) { + throw new IllegalArgumentException( + "Parameter 'value' cannot be null."); //$NON-NLS-1$ + } + + return Status.OK_STATUS; + } + + if (!( null !is cast(Number)value )) { + throw new IllegalArgumentException( + "Parameter 'value' is not of type Number."); //$NON-NLS-1$ + } + + Number number = cast(Number) value; + if (inRange(number)) { + return Status.OK_STATUS; + } + + synchronized (this) { + if (outOfRangeMessage is null && min !is null && max !is null) { + outOfRangeMessage = StringToNumberParser + .createOutOfRangeMessage(min, max, converter + .getNumberFormat()); + } + + return ValidationStatus.error(outOfRangeMessage); + } + } + + /** + * Invoked to determine if the value is in range. + * + * @param number + * @return true if in range + */ + protected abstract bool inRange(Number number); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToShortValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToShortValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToShortValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter; +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates if a Number can fit in a Short. + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public class NumberToShortValidator : NumberToNumberValidator { + private static final Short MIN = new Shortcast(Short.MIN_VALUE); + private static final Short MAX = new Shortcast(Short.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberToShortConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number) + */ + protected bool inRange(Number number) { + return StringToNumberParser.inShortRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToUnboundedNumberValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToUnboundedNumberValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.NumberToUnboundedNumberValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter; + +/** + * Validates if a Number can fit in an unbounded number (e.g. BigInteger, BigDecimal, etc.). + *

+ * Class is thread safe. + *

+ * + * @since 1.0 + */ +public class NumberToUnboundedNumberValidator : NumberToNumberValidator { + /** + * @param converter + */ + public this(NumberToNumberConverter converter) { + super(converter, null, null); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.validation.NumberToNumberValidator#inRange(java.lang.Number) + */ + protected bool inRange(Number number) { + return true; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ObjectToPrimitiveValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ObjectToPrimitiveValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Tom Schindl - bugfix for 217940 + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.ObjectToPrimitiveValidator; + +import java.lang.all; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * @since 3.2 + * + */ +public class ObjectToPrimitiveValidator : IValidator { + + private ClassInfo toType; + + private ClassInfo[][] primitiveMap = new ClassInfo[][] [ + [ Integer.TYPE, Integer.classinfo ], [ Short.TYPE, Short.classinfo ], + [ Long.TYPE, Long.classinfo ], [ Double.TYPE, Double.classinfo ], + [ Byte.TYPE, Byte.classinfo ], [ Float.TYPE, Float.classinfo ], + [ Boolean.TYPE, Boolean.classinfo ], + [ Character.TYPE, Character.classinfo ] ]; + + /** + * @param toType + */ + public this(ClassInfo toType) { + this.toType = toType; + } + + protected ClassInfo getToType() { + return this.toType; + } + + public IStatus validate(Object value) { + return doValidate(value); + } + + private IStatus doValidate(Object value) { + if (value !is null) { + if (!mapContainsValues(toType, value.getClass())) { + return ValidationStatus.error(getClassHint()); + } + return Status.OK_STATUS; + } + return ValidationStatus.error(getNullHint()); + } + + private bool mapContainsValues(ClassInfo toType, ClassInfo fromType) { + for (int i = 0; i < primitiveMap.length; i++) { + if ((primitiveMap[i][0].equals(toType)) + && (primitiveMap[i][1].equals(fromType))) { + return true; + } + } + return false; + } + + /** + * @return a hint string + */ + public String getNullHint() { + return BindingMessages.getStringcast(BindingMessages.VALIDATE_CONVERSION_TO_PRIMITIVE); + } + + /** + * @return a hint string + */ + public String getClassHint() { + return BindingMessages + .getStringcast(BindingMessages.VALIDATE_CONVERSION_FROM_CLASS_TO_PRIMITIVE); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ReadOnlyValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ReadOnlyValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,48 @@ +/* + * Copyright cast(C) 2005, 2006 db4objects Inc. (http://www.db4o.com) and others. + * + * 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: + * db4objects - Initial API and implementation + * Boris Bokowski (IBM Corporation) - bug 118429 + * Tom Schindl - bugfix for 217940 + */ +module org.eclipse.core.internal.databinding.validation.ReadOnlyValidator; + +import java.lang.all; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.runtime.IStatus; + +/** + * ReadOnlyValidator. A validator that can be used as a partial validator for read-only fields. + */ +public class ReadOnlyValidator : IValidator { + + private static ReadOnlyValidator singleton = null; + + /** + * Returns the ReadOnlyValidator + * + * @return the ReadOnlyValidator + */ + public static ReadOnlyValidator getDefault() { + if (singleton is null) { + singleton = new ReadOnlyValidator(); + } + return singleton; + } + + public IStatus validate(Object value) { + // No changes are allowed + return ValidationStatus.error(BindingMessages + .getStringcast(BindingMessages.VALIDATE_NO_CHANGE_ALLOWED_HELP)); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToByteValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToByteValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToByteValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * @since 1.0 + */ +public class StringToByteValidator : AbstractStringToNumberValidator { + private static final Byte MIN = new Bytecast(Byte.MIN_VALUE); + private static final Byte MAX = new Bytecast(Byte.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberFormatConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#isInRange(java.lang.Number) + */ + protected bool isInRange(Number number) { + return StringToNumberParser.inByteRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2007 Matt Carter and others. + * 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: + * Matt Carter - initial API and implementation + * Tom Schindl - bugfix for 217940 + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToCharacterValidator; + +import java.lang.all; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * Validates a String to Character conversion. + */ +public class StringToCharacterValidator : IValidator { + + private final StringToCharacterConverter converter; + + /** + * @param converter + */ + public this(StringToCharacterConverter converter) { + this.converter = converter; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object) + */ + public IStatus validate(Object value) { + try { + converter.convert(value); + } catch (IllegalArgumentException e) { + // The StringToCharacterConverter throws an IllegalArgumentException + // if it cannot convert. + return ValidationStatus.error(BindingMessages + .getStringcast(BindingMessages.VALIDATE_CHARACTER_HELP)); + } + return Status.OK_STATUS; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Tom Schindl - bugfix for 217940 + *******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToDateValidator; + +import java.lang.all; + +import java.util.Date; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.internal.databinding.BindingMessages; +import org.eclipse.core.internal.databinding.conversion.DateConversionSupport; +import org.eclipse.core.internal.databinding.conversion.StringToDateConverter; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; + +/** + * @since 1.0 + */ +public class StringToDateValidator : IValidator { + private final StringToDateConverter converter; + + /** + * @param converter + */ + public this(StringToDateConverter converter) { + this.converter = converter; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.validation.IValidator#validate(java.lang.Object) + */ + public IStatus validate(Object value) { + if ( null !is cast(String )value && (cast(String)value).trim().length() is 0) { + return Status.OK_STATUS; + } + Object convertedValue = converter.convert(value); + //The StringToDateConverter returns null if it can't parse the date. + if (convertedValue is null) { + return ValidationStatus.error(getErrorMessage()); + } + + return Status.OK_STATUS; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.validation.WrappedConverterValidator#getErrorMessage() + */ + protected String getErrorMessage() { + Date sampleDate = new Date(); + + // FIXME We need to use the information from the + // converter, not use another instance of DateConversionSupport. + FormatUtil util = new FormatUtil(); + StringBuffer samples = new StringBuffer(); + for (int formatterIdx = 1; formatterIdx < util.numFormatters() - 2; formatterIdx++) { + samples.append('\''); + samples.append(util.format(sampleDate, formatterIdx)); + samples.append("', "); //$NON-NLS-1$ + } + samples.append('\''); + samples.append(util.format(sampleDate, 0)); + samples.append('\''); + return BindingMessages.getStringcast(BindingMessages.EXAMPLES) + ": " + samples + ",..."; //$NON-NLS-1$//$NON-NLS-2$ + } + + private static class FormatUtil : DateConversionSupport { + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#numFormatters() + */ + protected int numFormatters() { + return super.numFormatters(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#format(java.util.Date) + */ + protected String format(Date date) { + return super.format(date); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.conversion.DateConversionSupport#format(java.util.Date, + * int) + */ + protected String format(Date date, int formatterIdx) { + return super.format(date, formatterIdx); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDoubleValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDoubleValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToDoubleValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * @since 1.0 + */ +public class StringToDoubleValidator : AbstractStringToNumberValidator { + private static final Double MIN = new Double(-Double.MAX_VALUE); + private static final Double MAX = new Doublecast(Double.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberFormatConverter converter) { + super(converter, MIN, MAX); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number) + */ + protected bool isInRange(Number number) { + return StringToNumberParser.inDoubleRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToFloatValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToFloatValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToFloatValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates that a string is of the appropriate format and is in the range of a + * float. + * + * @since 1.0 + */ +public class StringToFloatValidator : AbstractStringToNumberValidator { + + private static final Float MIN = new Float(-Float.MAX_VALUE); + private static final Float MAX = new Floatcast(Float.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberFormatConverter converter) { + super(converter, MIN, MAX); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number) + */ + protected bool isInRange(Number number) { + return StringToNumberParser.inFloatRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToIntegerValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + + +/** + * Validates that a string is of the appropriate format and is in the range of + * an integer. + * + * @since 1.0 + */ +public class StringToIntegerValidator : AbstractStringToNumberValidator { + private static final Integer MIN = new Integercast(Integer.MIN_VALUE); + private static final Integer MAX = new Integercast(Integer.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberFormatConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number) + */ + protected bool isInRange(Number number) { + return StringToNumberParser.inIntegerRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToLongValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToLongValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToLongValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * Validates that a string is of the appropriate format and is in the range of + * an long. + * + * @since 1.0 + */ +public class StringToLongValidator : AbstractStringToNumberValidator { + private static final Long MIN = new Longcast(Long.MIN_VALUE); + private static final Long MAX = new Longcast(Long.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberFormatConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number) + */ + protected bool isInRange(Number number) { + return StringToNumberParser.inLongRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToShortValidator.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToShortValidator.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.core.internal.databinding.validation.StringToShortValidator; + +import java.lang.all; + +import org.eclipse.core.internal.databinding.conversion.StringToNumberParser; + +/** + * @since 1.0 + */ +public class StringToShortValidator : AbstractStringToNumberValidator { + private static final Short MIN = new Shortcast(Short.MIN_VALUE); + private static final Short MAX = new Shortcast(Short.MAX_VALUE); + + /** + * @param converter + */ + public this(NumberFormatConverter converter) { + super(converter, MIN, MAX); + } + + /* (non-Javadoc) + * @see org.eclipse.core.internal.databinding.validation.AbstractStringToNumberValidator#inRange(java.lang.Number) + */ + protected bool isInRange(Number number) { + return StringToNumberParser.inShortRange(number); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.d --- a/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.d Sun Apr 12 12:27:13 2009 +0200 +++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.d Tue Apr 14 11:35:29 2009 +0200 @@ -58,7 +58,7 @@ } void openServices() { - implMissing(__FILE__,__LINE__); + //FIXME implMissing(__FILE__,__LINE__); // BundleContext context = JobActivator.getContext(); // if (context is null) { // if (JobManager.DEBUG) @@ -74,7 +74,7 @@ } void closeServices() { - implMissing(__FILE__,__LINE__); + //FIXME implMissing(__FILE__,__LINE__); // if (debugTracker !is null) { // debugTracker.close(); // debugTracker = null; @@ -86,7 +86,7 @@ } public bool getBooleanDebugOption(String option, bool defaultValue) { - implMissing(__FILE__,__LINE__); + //FIXME implMissing(__FILE__,__LINE__); return false; // if (debugTracker is null) { // if (JobManager.DEBUG) @@ -107,7 +107,7 @@ * null if the bundle could not be determined. */ public String getBundleId(Object object) { - implMissing(__FILE__,__LINE__); + //FIXME implMissing(__FILE__,__LINE__); // if (bundleTracker is null) { // if (JobManager.DEBUG) // JobMessages.message("Bundle tracker is not set"); //$NON-NLS-1$ @@ -133,8 +133,8 @@ * and false otherwise. */ bool useDaemonThreads() { - implMissing(__FILE__,__LINE__); - return false; + //FIXME implMissing(__FILE__,__LINE__); + return true; // BundleContext context = JobActivator.getContext(); // if (context is null) { // //we are running stand-alone, so consult global system property diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Semaphore.d --- a/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Semaphore.d Sun Apr 12 12:27:13 2009 +0200 +++ b/org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Semaphore.d Tue Apr 14 11:35:29 2009 +0200 @@ -37,10 +37,8 @@ */ public bool acquire(long delay) { synchronized(mutex){ - implMissing( __FILE__, __LINE__ ); -// SWT -// if (Thread.interrupted()) -// throw new InterruptedException(); + if (Thread.interrupted()) + throw new InterruptedException(); long start = System.currentTimeMillis(); long timeLeft = delay; while (true) { diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservable.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.jface.databinding.swt.ISWTObservable; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.swt.widgets.Widget; + +/** + * {@link IObservable} observing an SWT widget. + * + * @since 1.1 + * + */ +public interface ISWTObservable : IObservable { + + /** + * Returns the widget of this observable + * + * @return the widget + */ + public Widget getWidget(); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.jface.databinding.swt.ISWTObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.value.IObservableValue; + +/** + * {@link IObservableValue} observing an SWT widget. + * + * @since 1.1 + * + */ +public interface ISWTObservableValue : ISWTObservable, IObservableValue { + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/SWTObservables.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/SWTObservables.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,448 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matt Carter - bug 170668 + * Brad Reynolds - bug 170848 + * Matthew Hall - bug 180746, bug 207844 + * Michael Krauter, bug 180223 + *******************************************************************************/ +module org.eclipse.jface.databinding.swt.SWTObservables; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Iterator; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.jface.internal.databinding.internal.swt.LinkObservableValue; +import org.eclipse.jface.internal.databinding.swt.ButtonObservableValue; +import org.eclipse.jface.internal.databinding.swt.CComboObservableList; +import org.eclipse.jface.internal.databinding.swt.CComboObservableValue; +import org.eclipse.jface.internal.databinding.swt.CComboSingleSelectionObservableValue; +import org.eclipse.jface.internal.databinding.swt.CLabelObservableValue; +import org.eclipse.jface.internal.databinding.swt.ComboObservableList; +import org.eclipse.jface.internal.databinding.swt.ComboObservableValue; +import org.eclipse.jface.internal.databinding.swt.ComboSingleSelectionObservableValue; +import org.eclipse.jface.internal.databinding.swt.ControlObservableValue; +import org.eclipse.jface.internal.databinding.swt.DelayedObservableValue; +import org.eclipse.jface.internal.databinding.swt.LabelObservableValue; +import org.eclipse.jface.internal.databinding.swt.ListObservableList; +import org.eclipse.jface.internal.databinding.swt.ListObservableValue; +import org.eclipse.jface.internal.databinding.swt.ListSingleSelectionObservableValue; +import org.eclipse.jface.internal.databinding.swt.SWTProperties; +import org.eclipse.jface.internal.databinding.swt.ScaleObservableValue; +import org.eclipse.jface.internal.databinding.swt.ShellObservableValue; +import org.eclipse.jface.internal.databinding.swt.SpinnerObservableValue; +import org.eclipse.jface.internal.databinding.swt.TableSingleSelectionObservableValue; +import org.eclipse.jface.internal.databinding.swt.TextEditableObservableValue; +import org.eclipse.jface.internal.databinding.swt.TextObservableValue; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.Scale; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Spinner; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; + +/** + * A factory for creating observables for SWT widgets + * + * @since 1.1 + * + */ +public class SWTObservables { + + private static java.util.List realms = new ArrayList(); + + /** + * Returns the realm representing the UI thread for the given display. + * + * @param display + * @return the realm representing the UI thread for the given display + */ + public static Realm getRealm(Display display) { + synchronized (realms) { + for (Iterator it = realms.iterator(); it.hasNext();) { + DisplayRealm displayRealm = cast(DisplayRealm) it.next(); + if (displayRealm.display is display) { + return displayRealm; + } + } + DisplayRealm result = new DisplayRealm(display); + realms.add(result); + return result; + } + } + + /** + * Returns an observable which delays notification of value change events + * from observable until delay milliseconds + * have passed since the last change event, or until a FocusOut event is + * received from the underlying widget (whichever happens earlier). This + * class helps to delay validation until the user stops typing. To notify + * about pending changes, the returned observable value will fire a stale + * event when the wrapped observable value fires a change event, but this + * change is being delayed. + * + * @param delay + * @param observable + * @return an observable which delays notification of value change events + * from observable until delay + * milliseconds have passed since the last change event. + * + * @since 1.2 + */ + public static ISWTObservableValue observeDelayedValue(int delay, ISWTObservableValue observable) { + return new DelayedObservableValue(delay, observable); + } + + /** + * @param control + * @return an observable value tracking the enabled state of the given + * control + */ + public static ISWTObservableValue observeEnabled(Control control) { + return new ControlObservableValue(control, SWTProperties.ENABLED); + } + + /** + * @param control + * @return an observable value tracking the visible state of the given + * control + */ + public static ISWTObservableValue observeVisible(Control control) { + return new ControlObservableValue(control, SWTProperties.VISIBLE); + } + + /** + * @param control + * @return an observable value tracking the tooltip text of the given + * control + */ + public static ISWTObservableValue observeTooltipText(Control control) { + return new ControlObservableValue(control, SWTProperties.TOOLTIP_TEXT); + } + + /** + * Returns an observable observing the selection attribute of the provided + * control. The supported types are: + *
    + *
  • org.eclipse.swt.widgets.Spinner
  • + *
  • org.eclipse.swt.widgets.Button
  • + *
  • org.eclipse.swt.widgets.Combo
  • + *
  • org.eclipse.swt.custom.CCombo
  • + *
  • org.eclipse.swt.widgets.List
  • + *
  • org.eclipse.swt.widgets.Scale
  • + *
+ * + * @param control + * @return observable value + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static ISWTObservableValue observeSelection(Control control) { + if (null !is cast(Spinner)control) { + return new SpinnerObservableValue(cast(Spinner) control, + SWTProperties.SELECTION); + } else if (null !is cast(Button)control) { + return new ButtonObservableValue(cast(Button) control); + } else if (null !is cast(Combo)control) { + return new ComboObservableValue(cast(Combo) control, + SWTProperties.SELECTION); + } else if (null !is cast(CCombo)control) { + return new CComboObservableValue(cast(CCombo) control, + SWTProperties.SELECTION); + } else if (null !is cast(List)control) { + return new ListObservableValue(cast(List) control); + } else if (null !is cast(Scale)control) { + return new ScaleObservableValue(cast(Scale) control, + SWTProperties.SELECTION); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Returns an observable observing the minimum attribute of the provided + * control. The supported types are: + *
    + *
  • org.eclipse.swt.widgets.Spinner
  • + *
  • org.eclipse.swt.widgets.Scale
  • + *
+ * + * @param control + * @return observable value + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static ISWTObservableValue observeMin(Control control) { + if (null !is cast(Spinner)control) { + return new SpinnerObservableValue(cast(Spinner) control, + SWTProperties.MIN); + } else if (null !is cast(Scale)control) { + return new ScaleObservableValue(cast(Scale) control, SWTProperties.MIN); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Returns an observable observing the maximum attribute of the provided + * control. The supported types are: + *
    + *
  • org.eclipse.swt.widgets.Spinner
  • + *
  • org.eclipse.swt.widgets.Scale
  • + *
+ * + * @param control + * @return observable value + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static ISWTObservableValue observeMax(Control control) { + if (null !is cast(Spinner)control) { + return new SpinnerObservableValue(cast(Spinner) control, + SWTProperties.MAX); + } else if (null !is cast(Scale)control) { + return new ScaleObservableValue(cast(Scale) control, SWTProperties.MAX); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Returns an observable observing the text attribute of the provided + * control. The supported types are: + *
    + *
  • org.eclipse.swt.widgets.Text
  • + *
+ * + *
  • org.eclipse.swt.widgets.Label
  • + * @param control + * @param event event type to register for change events + * @return observable value + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static ISWTObservableValue observeText(Control control, int event) { + if (null !is cast(Text)control) { + return new TextObservableValue(cast(Text) control, event); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Returns an observable observing the text attribute of the provided + * control. The supported types are: + *
      + *
    • org.eclipse.swt.widgets.Label
    • + *
    • org.eclipse.swt.widgets.Link (as of 1.2)
    • + *
    • org.eclipse.swt.custom.Label
    • + *
    • org.eclipse.swt.widgets.Combo
    • + *
    • org.eclipse.swt.custom.CCombo
    • + *
    • org.eclipse.swt.widgets.Shell
    • + *
    + * + * @param control + * @return observable value + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static ISWTObservableValue observeText(Control control) { + if (null !is cast(Label)control) { + return new LabelObservableValue(cast(Label) control); + } else if (null !is cast(Link)control) { + return new LinkObservableValue(cast(Link) control); + } else if (null !is cast(CLabel)control) { + return new CLabelObservableValue(cast(CLabel) control); + } else if (null !is cast(Combo)control) { + return new ComboObservableValue(cast(Combo) control, SWTProperties.TEXT); + } else if (null !is cast(CCombo)control) { + return new CComboObservableValue(cast(CCombo) control, + SWTProperties.TEXT); + } else if (null !is cast(Shell)control) { + return new ShellObservableValue(cast(Shell) control); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Returns an observable observing the items attribute of the provided + * control. The supported types are: + *
      + *
    • org.eclipse.swt.widgets.Combo
    • + *
    • org.eclipse.swt.custom.CCombo
    • + *
    • org.eclipse.swt.widgets.List
    • + *
    + * + * @param control + * @return observable list + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static IObservableList observeItems(Control control) { + if (null !is cast(Combo)control) { + return new ComboObservableList(cast(Combo) control); + } else if (null !is cast(CCombo)control) { + return new CComboObservableList(cast(CCombo) control); + } else if (null !is cast(List)control) { + return new ListObservableList(cast(List) control); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * Returns an observable observing the single selection index attribute of + * the provided control. The supported types are: + *
      + *
    • org.eclipse.swt.widgets.Table
    • + *
    • org.eclipse.swt.widgets.Combo
    • + *
    • org.eclipse.swt.custom.CCombo
    • + *
    • org.eclipse.swt.widgets.List
    • + *
    + * + * @param control + * @return observable value + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static ISWTObservableValue observeSingleSelectionIndex( + Control control) { + if (null !is cast(Table)control) { + return new TableSingleSelectionObservableValue(cast(Table) control); + } else if (null !is cast(Combo)control) { + return new ComboSingleSelectionObservableValue(cast(Combo) control); + } else if (null !is cast(CCombo)control) { + return new CComboSingleSelectionObservableValue(cast(CCombo) control); + } else if (null !is cast(List)control) { + return new ListSingleSelectionObservableValue(cast(List) control); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + /** + * @param control + * @return an observable value tracking the foreground color of the given + * control + */ + public static ISWTObservableValue observeForeground(Control control) { + return new ControlObservableValue(control, SWTProperties.FOREGROUND); + } + + /** + * @param control + * @return an observable value tracking the background color of the given + * control + */ + public static ISWTObservableValue observeBackground(Control control) { + return new ControlObservableValue(control, SWTProperties.BACKGROUND); + } + + /** + * @param control + * @return an observable value tracking the font of the given control + */ + public static ISWTObservableValue observeFont(Control control) { + return new ControlObservableValue(control, SWTProperties.FONT); + } + + /** + * Returns an observable observing the editable attribute of + * the provided control. The supported types are: + *
      + *
    • org.eclipse.swt.widgets.Text
    • + *
    + * + * @param control + * @return observable value + * @throws IllegalArgumentException + * if control type is unsupported + */ + public static ISWTObservableValue observeEditable(Control control) { + if (null !is cast(Text)control) { + return new TextEditableObservableValue(cast(Text) control); + } + + throw new IllegalArgumentException( + "Widget [" + control.getClass().getName() + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + + private static class DisplayRealm : Realm { + private Display display; + + /** + * @param display + */ + private this(Display display) { + this.display = display; + } + + public bool isCurrent() { + return Display.getCurrent() is display; + } + + public void asyncExec(Runnable runnable) { + Runnable safeRunnable = dgRunnable((Runnable runnable_) { + safeRun(runnable_); + }, runnable); + if (!display.isDisposed()) { + display.asyncExec(safeRunnable); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return (display is null) ? 0 : display.hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public bool equals(Object obj) { + if (this is obj) + return true; + if (obj is null) + return false; + if (getClass() !is obj.getClass()) + return false; + final DisplayRealm other = cast(DisplayRealm) obj; + if (display is null) { + if (other.display !is null) + return false; + } else if (!display.equals(other.display)) + return false; + return true; + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides classes that can be used to observe changes in SWT widgets. +

    +Package Specification

    +

    +This package provides classes that can be used to observe changes in SWT widgets.

    + + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservable.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservable.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.IViewerObservable; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.jface.viewers.Viewer; + +/** + * {@link IObservable} observing a JFace Viewer. + * + * @since 1.2 + * + */ +public interface IViewerObservable : IObservable { + /** + * Returns the underlying viewer for this observable. + * + * @return the viewer. + */ + public Viewer getViewer(); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.IViewerObservableList; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.list.IObservableList; + +/** + * {@link IObservableList} observing a JFace Viewer. + * + * @since 1.2 + * + */ +public interface IViewerObservableList : IObservableList, + IViewerObservable { +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 124684) + ******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.IViewerObservableSet; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.set.IObservableSet; + +/** + * {@link IObservableSet} observing a JFace Viewer. + * + * @since 1.2 + * + */ +public interface IViewerObservableSet : IObservableSet, IViewerObservable { +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.IViewerObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.value.IObservableValue; + +/** + * {@link IObservableValue} observing a JFace Viewer. + * + * @since 1.2 + * + */ +public interface IViewerObservableValue : IObservableValue, + IViewerObservable { +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ListeningLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ListeningLabelProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.ListeningLabelProvider; + +import java.lang.all; + +import java.util.Iterator; + +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.jface.internal.databinding.provisional.viewers.ViewerLabelProvider; + +/** + * @since 1.1 + * + */ +public abstract class ListeningLabelProvider : ViewerLabelProvider { + + private ISetChangeListener listener = new class() ISetChangeListener { + public void handleSetChange(SetChangeEvent event) { + for (Iterator it = event.diff.getAdditions().iterator(); it.hasNext();) { + addListenerTo(it.next()); + } + for (Iterator it = event.diff.getRemovals().iterator(); it.hasNext();) { + removeListenerFrom(it.next()); + } + } + }; + + private IObservableSet items; + + /** + * @param itemsThatNeedLabels + */ + public this(IObservableSet itemsThatNeedLabels) { + this.items = itemsThatNeedLabels; + items.addSetChangeListener(listener); + for (Iterator it = items.iterator(); it.hasNext();) { + addListenerTo(it.next()); + } + } + + /** + * @param next + */ + protected abstract void removeListenerFrom(Object next); + + /** + * @param next + */ + protected abstract void addListenerTo(Object next); + + public void dispose() { + for (Iterator iter = items.iterator(); iter.hasNext();) { + removeListenerFrom(iter.next()); + } + items.removeSetChangeListener(listener); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2006-2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Tom Schindl - bugfix in: 214355 + * Matthew Hall - bugs 215531, 226765 + *******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.ObservableListContentProvider; + +import java.lang.all; + +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservableCollection; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiffVisitor; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.databinding.viewers.ObservableCollectionContentProvider; +import org.eclipse.jface.internal.databinding.viewers.ViewerElementSet; +import org.eclipse.jface.viewers.AbstractListViewer; +import org.eclipse.jface.viewers.AbstractTableViewer; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * A {@link IStructuredContentProvider content provider} for + * {@link AbstractTableViewer} or {@link AbstractListViewer} that provides + * elements of an {@link IObservableList} when set as the viewer's input. + * Objects of this class listen for changes to the observable list, and will + * insert and remove viewer elements to reflect observed changes. + * + *

    + * This class is not intended to be subclassed by clients. + * + * @since 1.1 + */ +public class ObservableListContentProvider : + IStructuredContentProvider { + private ObservableCollectionContentProvider impl; + + private static class Impl : ObservableCollectionContentProvider + , IListChangeListener { + protected void checkInput(Object input) { + Assert + .isTrue(null !is cast(IObservableList)input, + "This content provider only works with input of type IObservableList"); //$NON-NLS-1$ + } + + protected void addCollectionChangeListener( + IObservableCollection collection) { + (cast(IObservableList) collection).addListChangeListener(this); + } + + protected void removeCollectionChangeListener( + IObservableCollection collection) { + (cast(IObservableList) collection).removeListChangeListener(this); + } + + public void handleListChange(ListChangeEvent event) { + if (isViewerDisposed()) + return; + final Set removals = ViewerElementSet.withComparer(comparer); + + event.diff.accept(new class() ListDiffVisitor { + public void handleAdd(int index, Object element) { + knownElements.add(element); + viewerUpdater.insert(element, index); + } + + public void handleRemove(int index, Object element) { + viewerUpdater.remove(element, index); + removals.add(element); + } + + public void handleReplace(int index, Object oldElement, + Object newElement) { + knownElements.add(newElement); + viewerUpdater.replace(oldElement, newElement, index); + removals.add(oldElement); + } + + public void handleMove(int oldIndex, int newIndex, + Object element) { + viewerUpdater.move(element, oldIndex, newIndex); + } + }); + + // For each removed element, do not remove from known elements if + // the element is still present elsewhere in the list. + removals.removeAll(event.getObservableList()); + knownElements.removeAll(removals); + } + } + + /** + * Constructs an ObservableListContentProvider + */ + public this() { + impl = new Impl(); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + impl.inputChanged(viewer, oldInput, newInput); + } + + public Object[] getElements(Object inputElement) { + return impl.getElements(inputElement); + } + + public void dispose() { + impl.dispose(); + } + + /** + * Returns the set of elements known to this content provider. Label + * providers may track this set if they need to be notified about additions + * before the viewer sees the added element, and notified about removals + * after the element was removed from the viewer. This is intended for use + * by label providers, as it will always return the items that need labels. + * + * @return readableSet of items that will need labels + */ + public IObservableSet getKnownElements() { + return impl.getKnownElements(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListTreeContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListTreeContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2007-2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 207858) + * Matthew Hall - bug 226765 + *******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; + +import java.lang.all; + +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservableCollection; +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiffVisitor; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.jface.internal.databinding.viewers.ObservableCollectionTreeContentProvider; +import org.eclipse.jface.internal.databinding.viewers.ViewerElementSet; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * An {@link ITreeContentProvider} for use with an {@link AbstractTreeViewer}, + * which uses the provided {@link IObservableFactory list factory} to obtain the + * elements of a tree. Object of this class listen for changes to each + * {@link IObservableList} created by the factory, and will insert and remove + * viewer elements to reflect the observed changes. + * + *

    + * This class is not intended to be subclassed by clients. + * + * @since 1.2 + */ +public class ObservableListTreeContentProvider : ITreeContentProvider { + private final ObservableCollectionTreeContentProvider impl; + + private static class Impl : ObservableCollectionTreeContentProvider { + public this(IObservableFactory listFactory, + TreeStructureAdvisor structureAdvisor) { + super(listFactory, structureAdvisor); + } + + private class ListChangeListener : IListChangeListener { + final Object parentElement; + + public this(Object parentElement) { + this.parentElement = parentElement; + } + + public void handleListChange(ListChangeEvent event) { + if (isViewerDisposed()) + return; + + final Set removals = ViewerElementSet.withComparer(comparer); + event.diff.accept(new class() ListDiffVisitor { + public void handleAdd(int index, Object child) { + // adds to known elements if new element + getOrCreateNode(child).addParent(parentElement); + + viewerUpdater.insert(parentElement, child, index); + } + + public void handleRemove(int index, Object child) { + viewerUpdater.remove(parentElement, child, index); + + removals.add(child); + } + + public void handleReplace(int index, Object oldChild, + Object newChild) { + getOrCreateNode(newChild).addParent(parentElement); + + viewerUpdater.replace(parentElement, oldChild, + newChild, index); + + removals.add(oldChild); + } + + public void handleMove(int oldIndex, int newIndex, + Object child) { + viewerUpdater.move(parentElement, child, oldIndex, + newIndex); + } + }); + + // For each removed element, do not remove node's parent if the + // element is still present elsewhere in the list. + removals.removeAll(event.getObservableList()); + for (Iterator iterator = removals.iterator(); iterator + .hasNext();) { + TreeNode node = getExistingNode(iterator.next()); + if (node !is null) + // removes from known elements if last parent + node.removeParent(parentElement); + } + } + } + + protected IObservablesListener createCollectionChangeListener( + Object parentElement) { + return new ListChangeListener(parentElement); + } + + protected void addCollectionChangeListener( + IObservableCollection collection, IObservablesListener listener) { + IObservableList list = cast(IObservableList) collection; + IListChangeListener listListener = cast(IListChangeListener) listener; + list.addListChangeListener(listListener); + } + + protected void removeCollectionChangeListener( + IObservableCollection collection, IObservablesListener listener) { + IObservableList list = cast(IObservableList) collection; + IListChangeListener listListener = cast(IListChangeListener) listener; + list.removeListChangeListener(listListener); + } + } + + /** + * Constructs an ObservableListTreeContentProvider using the given list + * factory. + * + * @param listFactory + * observable factory that produces an IObservableList of + * children for a given parent element. Observable lists created + * by this factory must be on the realm of the current display. + * @param structureAdvisor + * an advisor that will be consulted from the implementations of + * the {@link #getParent(Object)} and + * {@link #hasChildren(Object)} methods, or null + * if no advisor is available. It is recommended that clients + * pass a non-null advisor if they can provide additional + * structural information about the tree. + */ + public this(IObservableFactory listFactory, + TreeStructureAdvisor structureAdvisor) { + impl = new Impl(listFactory, structureAdvisor); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + impl.inputChanged(viewer, oldInput, newInput); + } + + public Object[] getElements(Object inputElement) { + return impl.getElements(inputElement); + } + + public bool hasChildren(Object element) { + return impl.hasChildren(element); + } + + public Object[] getChildren(Object parentElement) { + return impl.getChildren(parentElement); + } + + public Object getParent(Object element) { + return impl.getParent(element); + } + + public void dispose() { + impl.dispose(); + } + + /** + * Returns the set of elements known to this content provider. Label + * providers may track this set if they need to be notified about additions + * before the viewer sees the added element, and notified about removals + * after the element was removed from the viewer. This is intended for use + * by label providers, as it will always return the items that need labels. + * + * @return readableSet of items that will need labels + */ + public IObservableSet getKnownElements() { + return impl.getKnownElements(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapLabelProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164247 + * Brad Reynolds - bug 164134 + *******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.ObservableMapLabelProvider; + +import java.lang.all; + +import java.util.Set; + +import org.eclipse.core.databinding.observable.map.IMapChangeListener; +import org.eclipse.core.databinding.observable.map.IObservableMap; +import org.eclipse.core.databinding.observable.map.MapChangeEvent; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.LabelProviderChangedEvent; +import org.eclipse.swt.graphics.Image; + +/** + * @since 1.1 + * + */ +public class ObservableMapLabelProvider : LabelProvider + , ILabelProvider, ITableLabelProvider { + + private final IObservableMap[] attributeMaps; + + private IMapChangeListener mapChangeListener = new class() IMapChangeListener { + public void handleMapChange(MapChangeEvent event) { + Set affectedElements = event.diff.getChangedKeys(); + LabelProviderChangedEvent newEvent = new LabelProviderChangedEvent( + this.outer, affectedElements + .toArray()); + fireLabelProviderChanged(newEvent); + } + }; + + /** + * @param attributeMap + */ + public this(IObservableMap attributeMap) { + this([ cast(IObservableMap)attributeMap ]); + } + + /** + * @param attributeMaps + */ + public this(IObservableMap[] attributeMaps) { + System.arraycopy(attributeMaps, 0, this.attributeMaps = attributeMaps, 0, attributeMaps.length); + for (int i = 0; i < attributeMaps.length; i++) { + attributeMaps[i].addMapChangeListener(mapChangeListener); + } + } + + public void dispose() { + for (int i = 0; i < attributeMaps.length; i++) { + attributeMaps[i].removeMapChangeListener(mapChangeListener); + } + super.dispose(); + } + + public Image getImage(Object element) { + return null; + } + + public String getText(Object element) { + return getColumnText(element, 0); + } + + public Image getColumnImage(Object element, int columnIndex) { + return null; + } + + public String getColumnText(Object element, int columnIndex) { + if (columnIndex < attributeMaps.length) { + Object result = attributeMaps[columnIndex].get(element); + return result is null ? "" : result.toString(); //$NON-NLS-1$ + } + return null; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 116920 + * Matthew Hall - bugs 215531, 226765 + *******************************************************************************/ +module org.eclipse.jface.databinding.viewers.ObservableSetContentProvider; + +import java.lang.all; + +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservableCollection; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.databinding.viewers.ObservableCollectionContentProvider; +import org.eclipse.jface.viewers.AbstractListViewer; +import org.eclipse.jface.viewers.AbstractTableViewer; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * A {@link IStructuredContentProvider content provider} for + * {@link AbstractTableViewer} or {@link AbstractListViewer} that provides + * elements of an {@link IObservableSet} when set as the viewer's input. Objects + * of this class listen for changes to the observable set, and will insert and + * remove viewer elements to reflect observed changes. + * + *

    + * This class is not intended to be subclassed by clients. + * + * @since 1.1 + */ +public class ObservableSetContentProvider : IStructuredContentProvider { + private ObservableCollectionContentProvider impl; + + private static class Impl : ObservableCollectionContentProvider + , ISetChangeListener { + protected void checkInput(Object input) { + Assert + .isTrue(null !is cast(IObservableSet)input, + "This content provider only works with input of type IObservableSet"); //$NON-NLS-1$ + } + + protected void addCollectionChangeListener( + IObservableCollection collection) { + (cast(IObservableSet) collection).addSetChangeListener(this); + } + + protected void removeCollectionChangeListener( + IObservableCollection collection) { + (cast(IObservableSet) collection).removeSetChangeListener(this); + } + + public void handleSetChange(SetChangeEvent event) { + if (isViewerDisposed()) + return; + + Set removals = event.diff.getRemovals(); + viewerUpdater.remove(removals.toArray()); + knownElements.removeAll(removals); + + Set additions = event.diff.getAdditions(); + knownElements.addAll(additions); + viewerUpdater.add(additions.toArray()); + } + } + + /** + * Constructs an ObservableSetContentProvider + */ + public this() { + impl = new Impl(); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + impl.inputChanged(viewer, oldInput, newInput); + } + + public Object[] getElements(Object inputElement) { + return impl.getElements(inputElement); + } + + public void dispose() { + impl.dispose(); + } + + /** + * Returns the set of elements known to this content provider. Label + * providers may track this set if they need to be notified about additions + * before the viewer sees the added element, and notified about removals + * after the element was removed from the viewer. This is intended for use + * by label providers, as it will always return the items that need labels. + * + * @return unmodifiable set of items that will need labels + */ + public IObservableSet getKnownElements() { + return impl.getKnownElements(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetTreeContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetTreeContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 207858) + * Matthew Hall - bug 226765 + *******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider; + +import java.lang.all; + +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservableCollection; +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.jface.internal.databinding.viewers.ObservableCollectionTreeContentProvider; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +/** + * An {@link ITreeContentProvider} for use with an {@link AbstractTreeViewer}, + * which uses the provided {@link IObservableFactory set factory} to obtain the + * elements of a tree. Objects of this class listen for changes to each + * {@link IObservableSet} created by the factory, and will insert and remove + * viewer elements to reflect the observed changes. + * + *

    + * This class is not intended to be subclassed by clients. + * + * @since 1.2 + */ +public class ObservableSetTreeContentProvider : ITreeContentProvider { + private final ObservableCollectionTreeContentProvider impl; + + private static class Impl : ObservableCollectionTreeContentProvider { + this(IObservableFactory setFactory, + TreeStructureAdvisor structureAdvisor) { + super(setFactory, structureAdvisor); + } + + private class SetChangeListener : ISetChangeListener { + final Object parentElement; + + public this(Object parentElement) { + this.parentElement = parentElement; + } + + public void handleSetChange(SetChangeEvent event) { + if (isViewerDisposed()) + return; + + Set removals = event.diff.getRemovals(); + if (!removals.isEmpty()) { + viewerUpdater.remove(parentElement, removals.toArray()); + for (Iterator iterator = removals.iterator(); iterator + .hasNext();) { + Object child = iterator.next(); + TreeNode childNode = getExistingNode(child); + if (childNode !is null) + childNode.removeParent(parentElement); + } + } + + Set additions = event.diff.getAdditions(); + if (!additions.isEmpty()) { + for (Iterator iterator = additions.iterator(); iterator + .hasNext();) { + Object child = iterator.next(); + getOrCreateNode(child).addParent(parentElement); + } + viewerUpdater.add(parentElement, additions.toArray()); + } + } + } + + protected IObservablesListener createCollectionChangeListener( + Object parentElement) { + return new SetChangeListener(parentElement); + } + + protected void addCollectionChangeListener( + IObservableCollection collection, IObservablesListener listener) { + IObservableSet set = cast(IObservableSet) collection; + ISetChangeListener setListener = cast(ISetChangeListener) listener; + set.addSetChangeListener(setListener); + } + + protected void removeCollectionChangeListener( + IObservableCollection collection, IObservablesListener listener) { + IObservableSet set = cast(IObservableSet) collection; + ISetChangeListener setListener = cast(ISetChangeListener) listener; + set.removeSetChangeListener(setListener); + } + } + + /** + * Constructs an ObservableListTreeContentProvider using the given list + * factory. + * + * @param setFactory + * observable factory that produces an IObservableSet of children + * for a given parent element. Observable sets created by this + * factory must be on the realm of the current display. + * @param structureAdvisor + * an advisor that will be consulted from the implementations of + * the {@link #getParent(Object)} and + * {@link #hasChildren(Object)} methods, or null + * if no advisor is available. It is recommended that clients + * pass a non-null advisor if they can provide additional + * structural information about the tree. + */ + public this(IObservableFactory setFactory, + TreeStructureAdvisor structureAdvisor) { + impl = new Impl(setFactory, structureAdvisor); + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + impl.inputChanged(viewer, oldInput, newInput); + } + + public Object[] getElements(Object inputElement) { + return impl.getElements(inputElement); + } + + public bool hasChildren(Object element) { + return impl.hasChildren(element); + } + + public Object[] getChildren(Object parentElement) { + return impl.getChildren(parentElement); + } + + public Object getParent(Object element) { + return impl.getParent(element); + } + + public void dispose() { + impl.dispose(); + } + + /** + * Returns the set of elements known to this content provider. Label + * providers may track this set if they need to be notified about additions + * before the viewer sees the added element, and notified about removals + * after the element was removed from the viewer. This is intended for use + * by label providers, as it will always return the items that need labels. + * + * @return readableSet of items that will need labels + */ + public IObservableSet getKnownElements() { + return impl.getKnownElements(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableValueEditingSupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableValueEditingSupport.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.ObservableValueEditingSupport; + +import java.lang.all; + +import org.eclipse.core.databinding.Binding; +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.UpdateValueStrategy; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ColumnViewer; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationEvent; +import org.eclipse.jface.viewers.ColumnViewerEditorActivationListener; +import org.eclipse.jface.viewers.ColumnViewerEditorDeactivationEvent; +import org.eclipse.jface.viewers.EditingSupport; +import org.eclipse.jface.viewers.ViewerCell; + +/** + * {@link EditingSupport} using the JFace Data Binding concepts to handle the + * updating of an element from a {@link CellEditor}. + * + * @since 1.2 + */ +public abstract class ObservableValueEditingSupport : EditingSupport { + /** + * Maintains references to the instances currently imployed while editing. + * Will be null when not editing. + */ + private EditingState editingState; + + private final ColumnViewerEditorActivationListenerHelper activationListener = new ColumnViewerEditorActivationListenerHelper(); + + private ColumnViewer viewer; + + private DataBindingContext dbc; + + /** + * Constructs a new instance with the provided viewer and + * dbc. + * + * @param viewer + * viewer to edit + * @param dbc + * dbc to create Bindings + */ + public this(ColumnViewer viewer, + DataBindingContext dbc) { + super(viewer); + + if (dbc is null) { + throw new IllegalArgumentException("Parameter dbc was null."); //$NON-NLS-1$ + } + + this.viewer = viewer; + this.dbc = dbc; + } + + /** + * Default implementation always returns true. + * + * @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object) + */ + protected bool canEdit(Object element) { + return true; + } + + /** + * Default implementation always returns null as this will be + * handled by the Binding. + * + * @see org.eclipse.jface.viewers.EditingSupport#getValue(java.lang.Object) + */ + protected Object getValue(Object element) { + // no op + return null; + } + + /** + * Default implementation does nothing as this will be handled by the + * Binding. + * + * @see org.eclipse.jface.viewers.EditingSupport#setValue(java.lang.Object, + * java.lang.Object) + */ + protected void setValue(Object element, Object value) { + // no op + } + + /** + * Creates a {@link Binding} between the editor and the element to be + * edited. Invokes {@link #doCreateCellEditorObservable(CellEditor)}, + * {@link #doCreateElementObservable(Object, ViewerCell)}, and then + * {@link #createBinding(IObservableValue, IObservableValue)}. + */ + final protected void initializeCellEditorValue(CellEditor cellEditor, + ViewerCell cell) { + IObservableValue target = doCreateCellEditorObservable(cellEditor); + Assert + .isNotNull(target, + "doCreateCellEditorObservable(...) did not return an observable"); //$NON-NLS-1$ + + IObservableValue model = doCreateElementObservable(cell.getElement(), + cell); + Assert.isNotNull(model, + "doCreateElementObservable(...) did not return an observable"); //$NON-NLS-1$ + + Binding binding = createBinding(target, model); + Assert + .isNotNull(binding, + "createBinding(...) did not return a binding"); //$NON-NLS-1$ + + editingState = new EditingState(binding, target, model); + + getViewer().getColumnViewerEditor().addEditorActivationListener( + activationListener); + } + + /** + * Creates the observable value for the CellEditor. + * + * @param cellEditor + * @return observable value + */ + protected abstract IObservableValue doCreateCellEditorObservable( + CellEditor cellEditor); + + /** + * Creates the observable value for the element. + * + * @param element + * @param cell + * @return observable value + */ + protected abstract IObservableValue doCreateElementObservable( + Object element, ViewerCell cell); + + /** + * Creates a new binding for the provided target and + * model. Default + * {@link UpdateValueStrategy value update strategies} are used with the + * target to model updating on {@link UpdateValueStrategy#POLICY_CONVERT}. + * + * @param target + * @param model + * @return binding + */ + protected Binding createBinding(IObservableValue target, + IObservableValue model) { + return dbc.bindValue(target, model, new UpdateValueStrategy( + UpdateValueStrategy.POLICY_CONVERT), null); + } + + /** + * Updates the model from the target. + */ + final protected void saveCellEditorValue(CellEditor cellEditor, + ViewerCell cell) { + editingState.binding.updateTargetToModel(); + } + + private class ColumnViewerEditorActivationListenerHelper : + ColumnViewerEditorActivationListener { + + public void afterEditorActivated(ColumnViewerEditorActivationEvent event) { + // do nothing + } + + public void afterEditorDeactivated( + ColumnViewerEditorDeactivationEvent event) { + editingState.dispose(); + editingState = null; + + viewer.getColumnViewerEditor().removeEditorActivationListener(this); + } + + public void beforeEditorActivated( + ColumnViewerEditorActivationEvent event) { + // do nothing + } + + public void beforeEditorDeactivated( + ColumnViewerEditorDeactivationEvent event) { + // do nothing + } + } + + /** + * Maintains references to objects that only live for the length of the edit + * cycle. + */ + private static class EditingState { + IObservableValue target; + + IObservableValue model; + + Binding binding; + + this(Binding binding, IObservableValue target, + IObservableValue model) { + this.binding = binding; + this.target = target; + this.model = model; + } + + void dispose() { + target.dispose(); + model.dispose(); + binding.dispose(); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/TreeStructureAdvisor.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/TreeStructureAdvisor.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + ******************************************************************************/ +module org.eclipse.jface.databinding.viewers.TreeStructureAdvisor; + +import java.lang.all; + +/** + * Instances of this class can be used to improve accuracy and performance of an + * {@link ObservableListTreeContentProvider} or an + * {@link ObservableSetTreeContentProvider}. This class is intended to be + * subclassed by clients. + * + * @since 1.2 + * + */ +public abstract class TreeStructureAdvisor { + + /** + * Returns the parent for the given element, or null + * indicating that the parent can't be computed. In this case the + * tree-structured viewer can't expand a given node correctly if requested. + * The default implementation returns null; clients should override. + * + * @param element + * the element + * @return the parent element, or null if it has none or if + * the parent cannot be computed + */ + public Object getParent(Object element) { + return null; + } + + /** + * Returns whether the given element has children, or null if + * the actual children collection should be consulted. The default + * implementation returns null; clients should override. + *

    + * Intended as an optimization for when the viewer does not need the actual + * children. Clients may be able to implement this more efficiently than + * getChildren. + *

    + * + * @param element + * the element + * @return Boolean.TRUE if the given element has children, + * Boolean.FALSE if it has no children, or + * null if the children collection should be + * consulted. + */ + public Boolean hasChildren(Object element) { + return null; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewersObservables.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewersObservables.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,220 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 206839 + * Matthew Hall - bug 124684 + *******************************************************************************/ + +module org.eclipse.jface.databinding.viewers.ViewersObservables; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.jface.internal.databinding.viewers.CheckableCheckedElementsObservableSet; +import org.eclipse.jface.internal.databinding.viewers.CheckboxViewerCheckedElementsObservableSet; +import org.eclipse.jface.internal.databinding.viewers.SelectionProviderMultipleSelectionObservableList; +import org.eclipse.jface.internal.databinding.viewers.SelectionProviderSingleSelectionObservableValue; +import org.eclipse.jface.internal.databinding.viewers.ViewerInputObservableValue; +import org.eclipse.jface.internal.databinding.viewers.ViewerMultipleSelectionObservableList; +import org.eclipse.jface.internal.databinding.viewers.ViewerSingleSelectionObservableValue; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.ICheckable; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Display; + +/** + * Factory methods for creating observables for JFace viewers + * + * @since 1.1 + */ +public class ViewersObservables { + + /** + * Returns an observable value that tracks the current selection of the + * given selection provider. If the selection provider provides selections + * of type {@link IStructuredSelection}, the observable value will be the + * first element of the structured selection as returned by + * {@link IStructuredSelection#getFirstElement()}. + * + * @param selectionProvider + * @return the observable value tracking the (single) selection of the given + * selection provider + */ + public static IObservableValue observeSingleSelection( + ISelectionProvider selectionProvider) { + if (null !is cast(Viewer)selectionProvider) { + return observeSingleSelection(cast(Viewer) selectionProvider); + } + return new SelectionProviderSingleSelectionObservableValue( + SWTObservables.getRealm(Display.getDefault()), + selectionProvider); + } + + /** + * Returns an observable list that tracks the current selection of the + * given selection provider. Assumes that the selection provider provides + * selections of type {@link IStructuredSelection}. Note that the + * observable list will not honor the full contract of + * java.util.List in that it may delete or reorder elements + * based on what the selection provider returns from + * {@link ISelectionProvider#getSelection()} after having called + * {@link ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)} + * based on the requested change to the observable list. The affected + * methods are add, addAll, and + * set. + * + * @param selectionProvider + * @return the observable value tracking the (multi) selection of the given + * selection provider + * + * @since 1.2 + */ + public static IObservableList observeMultiSelection( + ISelectionProvider selectionProvider) { + if (null !is cast(Viewer)selectionProvider) { + return observeMultiSelection(cast(Viewer) selectionProvider); + } + return new SelectionProviderMultipleSelectionObservableList( + SWTObservables.getRealm(Display.getDefault()), + selectionProvider, Object.classinfo); + } + + /** + * Returns an observable value that tracks the current selection of the + * given viewer. If the viewer provides selections of type + * {@link IStructuredSelection}, the observable value will be the first + * element of the structured selection as returned by + * {@link IStructuredSelection#getFirstElement()}. + * + * @param viewer + * the viewer + * @return the observable value tracking the (single) selection of the given + * viewer + * @since 1.2 + */ + public static IViewerObservableValue observeSingleSelection(Viewer viewer) { + return new ViewerSingleSelectionObservableValue( + SWTObservables.getRealm(Display.getDefault()), + viewer); + } + + /** + * Returns an observable list that tracks the current selection of the + * given viewer. Assumes that the viewer provides + * selections of type {@link IStructuredSelection}. Note that the + * observable list will not honor the full contract of + * java.util.List in that it may delete or reorder elements + * based on what the viewer returns from + * {@link ISelectionProvider#getSelection()} after having called + * {@link ISelectionProvider#setSelection(org.eclipse.jface.viewers.ISelection)} + * based on the requested change to the observable list. The affected + * methods are add, addAll, and + * set. + * + * @param viewer + * @return the observable value tracking the (multi) selection of the given + * selection provider + * + * @since 1.2 + */ + public static IViewerObservableList observeMultiSelection( + Viewer viewer) { + return new ViewerMultipleSelectionObservableList( + SWTObservables.getRealm(Display.getDefault()), + viewer, Object.classinfo); + } + + /** + * Returns an observable value that tracks the input of the given viewer. + *

    + * The returned observer is blind to changes in the viewer's input unless + * its {@link IObservableValue#setValue(Object)} method is called directly. + * + * @param viewer + * the viewer to observe + * @return an observable value tracking the input of the given viewer + * @since 1.2 + */ + public static IObservableValue observeInput(Viewer viewer) { + return new ViewerInputObservableValue(SWTObservables.getRealm(viewer + .getControl().getDisplay()), viewer); + } + + /** + * Returns an observable set that tracks the checked elements of the given + * ICheckable. + * + * @param checkable + * {@link ICheckable} containing the checked elements to track + * @param elementType + * element type of the returned set + * @return an observable set tracking the checked elements of the given + * checkable. + * @since 1.2 + */ + public static IObservableSet observeCheckedElements(ICheckable checkable, + Object elementType) { + if (null !is cast(CheckboxTableViewer)checkable) { + return observeCheckedElements(cast(CheckboxTableViewer) checkable, + elementType); + } + if (null !is cast(CheckboxTreeViewer)checkable) { + return observeCheckedElements(cast(CheckboxTreeViewer) checkable, + elementType); + } + return new CheckableCheckedElementsObservableSet(SWTObservables + .getRealm(Display.getDefault()), checkable, elementType); + } + + /** + * Returns an observable set that tracks the checked elements of the given + * viewer. Assumes that the viewer implements {@link ICheckable}. + * + * @param viewer + * {@link CheckboxTableViewer} containing the checked elements to + * track. + * @param elementType + * element type of the returned set + * @return an observable set that tracks the checked elements of the given + * viewer. + * @since 1.2 + */ + public static IViewerObservableSet observeCheckedElements( + CheckboxTableViewer viewer, Object elementType) { + return new CheckboxViewerCheckedElementsObservableSet(SWTObservables + .getRealm(viewer.getControl().getDisplay()), viewer, + elementType); + } + + /** + * Returns an observable set that tracks the checked elements of the given + * viewer. Assumes that the viewer implements {@link ICheckable}. + * + * @param viewer + * {@link CheckboxTreeViewer} containing the checked elements to + * track. + * @param elementType + * element type of the returned set + * @return an observable set that tracks the checked elements of the given + * viewer. + * @since 1.2 + */ + public static IViewerObservableSet observeCheckedElements( + CheckboxTreeViewer viewer, Object elementType) { + return new CheckboxViewerCheckedElementsObservableSet(SWTObservables + .getRealm(viewer.getControl().getDisplay()), viewer, + elementType); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides classes that can be used to observe the JFace Viewer framework. +

    +Package Specification

    +

    +This package provides classes that can be used to observe the JFace Viewer framework.

    + + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/WizardPageSupport.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/WizardPageSupport.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,301 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Boris Bokowski - bug 218269 + * Matthew Hall - bug 218269 + * Ashley Cambrell - bug 199179 + *******************************************************************************/ +module org.eclipse.jface.databinding.wizard.WizardPageSupport; + +import java.lang.all; + +import java.util.Iterator; + +import org.eclipse.core.databinding.AggregateValidationStatus; +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.ValidationStatusProvider; +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiff; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.runtime.AssertionFailedException; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.wizard.WizardPage; + +/** + * Connects the validation result from the given data binding context to the + * given wizard page, updating the wizard page's completion state and its error + * message accordingly. + * + * @noextend This class is not intended to be subclassed by clients. + * + * @since 1.1 + */ +public class WizardPageSupport { + + private WizardPage wizardPage; + private DataBindingContext dbc; + private AggregateValidationStatus aggregateStatus; + private bool uiChanged = false; + + /** + * Connect the validation result from the given data binding context to the + * given wizard page. Upon creation, the wizard page support will use the + * context's validation result to determine whether the page is complete. + * The page's error message will not be set at this time ensuring that the + * wizard page does not show an error right away. Upon any validation result + * change, {@link WizardPage#setPageComplete(bool)} will be called + * reflecting the new validation result, and the wizard page's error message + * will be updated according to the current validation result. + * + * @param wizardPage + * @param dbc + * @return an instance of WizardPageSupport + */ + public static WizardPageSupport create(WizardPage wizardPage, + DataBindingContext dbc) { + return new WizardPageSupport(wizardPage, dbc); + } + + private this(WizardPage wizardPage, DataBindingContext dbc) { + this.wizardPage = wizardPage; + this.dbc = dbc; + init(); + } + + private IChangeListener uiChangeListener = new class() IChangeListener { + public void handleChange(ChangeEvent event) { + handleUIChanged(); + } + }; + private IListChangeListener validationStatusProvidersListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + ListDiff diff = event.diff; + ListDiffEntry[] differences = diff.getDifferences(); + for (int i = 0; i < differences.length; i++) { + ListDiffEntry listDiffEntry = differences[i]; + ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) listDiffEntry + .getElement(); + IObservableList targets = validationStatusProvider.getTargets(); + if (listDiffEntry.isAddition()) { + targets + .addListChangeListener(validationStatusProviderTargetsListener); + for (Iterator it = targets.iterator(); it.hasNext();) { + (cast(IObservable) it.next()) + .addChangeListener(uiChangeListener); + } + } else { + targets + .removeListChangeListener(validationStatusProviderTargetsListener); + for (Iterator it = targets.iterator(); it.hasNext();) { + (cast(IObservable) it.next()) + .removeChangeListener(uiChangeListener); + } + } + } + } + }; + private IListChangeListener validationStatusProviderTargetsListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + ListDiff diff = event.diff; + ListDiffEntry[] differences = diff.getDifferences(); + for (int i = 0; i < differences.length; i++) { + ListDiffEntry listDiffEntry = differences[i]; + IObservable target = cast(IObservable) listDiffEntry.getElement(); + if (listDiffEntry.isAddition()) { + target.addChangeListener(uiChangeListener); + } else { + target.removeChangeListener(uiChangeListener); + } + } + } + }; + private IStatus currentStatus; + + protected void init() { + aggregateStatus = new AggregateValidationStatus(dbc + .getValidationStatusProviders(), + AggregateValidationStatus.MAX_SEVERITY); + aggregateStatus.addValueChangeListener(new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + + currentStatus = cast(IStatus) event.diff.getNewValue(); + handleStatusChanged(); + } + }); + currentStatus = cast(IStatus) aggregateStatus.getValue(); + handleStatusChanged(); + dbc.getValidationStatusProviders().addListChangeListener( + validationStatusProvidersListener); + for (Iterator it = dbc.getValidationStatusProviders().iterator(); it + .hasNext();) { + ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) it + .next(); + IObservableList targets = validationStatusProvider.getTargets(); + targets + .addListChangeListener(validationStatusProviderTargetsListener); + for (Iterator iter = targets.iterator(); iter.hasNext();) { + (cast(IObservable) iter.next()).addChangeListener(uiChangeListener); + } + } + } + + protected void handleUIChanged() { + uiChanged = true; + if (currentStatus !is null) { + handleStatusChanged(); + } + dbc.getValidationStatusProviders().removeListChangeListener( + validationStatusProvidersListener); + for (Iterator it = dbc.getValidationStatusProviders().iterator(); it + .hasNext();) { + ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) it + .next(); + IObservableList targets = validationStatusProvider.getTargets(); + targets + .removeListChangeListener(validationStatusProviderTargetsListener); + for (Iterator iter = targets.iterator(); iter.hasNext();) { + (cast(IObservable) iter.next()) + .removeChangeListener(uiChangeListener); + } + } + } + + protected void handleStatusChanged() { + if (currentStatus !is null + && currentStatus.getSeverity() is IStatus.ERROR) { + wizardPage.setPageComplete(false); + wizardPage.setMessage(null); + wizardPage.setErrorMessage(uiChanged ? currentStatus.getMessage() + : null); + if (currentStatusHasException()) { + handleStatusException(); + } + } else if (currentStatus !is null + && currentStatus.getSeverity() !is IStatus.OK) { + int severity = currentStatus.getSeverity(); + wizardPage.setPageComplete((severity & IStatus.CANCEL) !is 0); + int type; + switch (severity) { + case IStatus.OK: + type = IMessageProvider.NONE; + break; + case IStatus.CANCEL: + type = IMessageProvider.NONE; + break; + case IStatus.INFO: + type = IMessageProvider.INFORMATION; + break; + case IStatus.WARNING: + type = IMessageProvider.WARNING; + break; + case IStatus.ERROR: + type = IMessageProvider.ERROR; + break; + default: + throw new AssertionFailedException( + "incomplete switch statement"); //$NON-NLS-1$ + } + wizardPage.setErrorMessage(null); + wizardPage.setMessage(currentStatus.getMessage(), type); + } else { + wizardPage.setPageComplete(true); + wizardPage.setMessage(null); + wizardPage.setErrorMessage(null); + } + } + + private bool currentStatusHasException() { + bool hasException = false; + if (currentStatus.getException() !is null) { + hasException = true; + } + if (null !is cast(MultiStatus)currentStatus) { + MultiStatus multiStatus = cast(MultiStatus) currentStatus; + + for (int i = 0; i < multiStatus.getChildren().length; i++) { + IStatus status = multiStatus.getChildren()[i]; + if (status.getException() !is null) { + hasException = true; + break; + } + } + } + return hasException; + } + + /** + * This is called when a Override to provide custom exception handling and + * reporting. + */ + protected void handleStatusException() { + if (currentStatus.getException() !is null) { + logThrowable(currentStatus.getException()); + } else if (null !is cast(MultiStatus)currentStatus) { + MultiStatus multiStatus = cast(MultiStatus) currentStatus; + for (int i = 0; i < multiStatus.getChildren().length; i++) { + IStatus status = multiStatus.getChildren()[i]; + if (status.getException() !is null) { + logThrowable(status.getException()); + } + } + } + } + + private void logThrowable(Throwable throwable) { + Policy + .getLog() + .log( + new Status( + IStatus.ERROR, + Policy.JFACE_DATABINDING, + IStatus.OK, + "Unhandled exception: " + throwable.getMessage(), throwable)); //$NON-NLS-1$ + } + + /** + * Disposes of this wizard page support object, removing any listeners it + * may have attached. + */ + public void dispose() { + aggregateStatus.dispose(); + if (!uiChanged) { + for (Iterator it = dbc.getValidationStatusProviders().iterator(); it + .hasNext();) { + ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) it + .next(); + IObservableList targets = validationStatusProvider.getTargets(); + targets + .removeListChangeListener(validationStatusProviderTargetsListener); + for (Iterator iter = targets.iterator(); iter.hasNext();) { + (cast(IObservable) iter.next()) + .removeChangeListener(uiChangeListener); + } + } + dbc.getValidationStatusProviders().removeListChangeListener( + validationStatusProvidersListener); + } + aggregateStatus = null; + dbc = null; + uiChangeListener = null; + validationStatusProvidersListener = null; + validationStatusProviderTargetsListener = null; + wizardPage = null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/package.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/package.html Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,16 @@ + + + + + + + Package-level Javadoc + + +Provides classes that bridge between data binding and the JFace Wizard framework. +

    +Package Specification

    +

    +This package provides classes that bridge between data binding and the JFace Wizard framework.

    + + diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/internal/swt/LinkObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/internal/swt/LinkObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2008 Michael Krauter, Catuno GmbH and others. + * 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: + * Michael Krauter, Catuno GmbH - initial API and implementation (bug 180223) + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.internal.swt.LinkObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.widgets.Link; + +/** + * @since 1.2 + * + */ +public class LinkObservableValue : AbstractSWTObservableValue { + + private final Link link; + + /** + * @param link + */ + public this(Link link) { + super(link); + this.link = link; + } + + public void doSetValue(Object value) { + String oldValue = link.getText(); + link.setText(value is null ? "" : value.toString()); //$NON-NLS-1$ + fireValueChange(Diffs.createValueDiff(oldValue, link.getText())); + } + + public Object doGetValue() { + return link.getText(); + } + + public Object getValueType() { + return String.classinfo; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/AbstractSWTObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/AbstractSWTObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2006 The Pampered Chef, Inc. and others. + * 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: + * The Pampered Chef, Inc. - initial API and implementation + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; +import org.eclipse.jface.databinding.swt.ISWTObservableValue; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Widget; + +/** + * NON-API - An abstract superclass for observable values that gurantees that the + * observable will be disposed when the control to which it is attached is + * disposed. + * + * @since 1.1 + */ +public abstract class AbstractSWTObservableValue : AbstractObservableValue , ISWTObservableValue { + + private final Widget widget; + + /** + * Standard constructor for an SWT ObservableValue. Makes sure that + * the observable gets disposed when the SWT widget is disposed. + * + * @param widget + */ + protected this(Widget widget) { + this(SWTObservables.getRealm(widget.getDisplay()), widget); + } + + /** + * Constructor that allows for the setting of the realm. Makes sure that the + * observable gets disposed when the SWT widget is disposed. + * + * @param realm + * @param widget + * @since 1.2 + */ + protected this(Realm realm, Widget widget) { + super(realm); + this.widget = widget; + widget.addDisposeListener(disposeListener); + } + + private DisposeListener disposeListener = new class() DisposeListener { + public void widgetDisposed(DisposeEvent e) { + this.outer.dispose(); + } + }; + + /** + * @return Returns the widget. + */ + public Widget getWidget() { + return widget; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/AbstractSWTVetoableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/AbstractSWTVetoableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2006 The Pampered Chef, Inc. and others. + * 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: + * The Pampered Chef, Inc. - initial API and implementation + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTVetoableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.value.AbstractVetoableValue; +import org.eclipse.jface.databinding.swt.ISWTObservableValue; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Widget; + +/** + * NON-API - An abstract superclass for vetoable values that gurantees that the + * observable will be disposed when the control to which it is attached is + * disposed. + * + * @since 1.1 + */ +public abstract class AbstractSWTVetoableValue : AbstractVetoableValue , ISWTObservableValue { + + private final Widget widget; + + /** + * Standard constructor for an SWT VetoableValue. Makes sure that + * the observable gets disposed when the SWT widget is disposed. + * + * @param widget + */ + protected this(Widget widget) { + this(SWTObservables.getRealm(widget.getDisplay()), widget); + } + + /** + * Constructs a new instance for the provided realm and widget. + * + * @param realm + * @param widget + * @since 1.2 + */ + protected this(Realm realm, Widget widget) { + super(realm); + this.widget = widget; + if (widget is null) { + throw new IllegalArgumentException("The widget parameter is null."); //$NON-NLS-1$ + } + widget.addDisposeListener(disposeListener); + } + + private DisposeListener disposeListener = new class() DisposeListener { + public void widgetDisposed(DisposeEvent e) { + this.outer.dispose(); + } + }; + + /** + * @return Returns the widget. + */ + public Widget getWidget() { + return widget; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/CompositeUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/CompositeUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,245 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * Boris Bokowski, IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.swt.CompositeUpdater; + +import java.lang.all; + +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Widget; + +/** + * NON-API - This class can be used to update a composite with automatic dependency tracking. + * @since 1.1 + * + */ +public abstract class CompositeUpdater { + + private class UpdateRunnable : Runnable, IChangeListener { + private Widget widget; + Object element; + + private bool dirty = true; + + private IObservable[] dependencies = new IObservable[0]; + + this(Widget widget, Object element) { + this.widget = widget; + this.element = element; + } + + // Runnable implementation. This method runs at most once per repaint + // whenever the + // value gets marked as dirty. + public void run() { + if (theComposite !is null && !theComposite.isDisposed() + && widget !is null && !widget.isDisposed()) { + updateIfNecessary(); + } + } + + private void updateIfNecessary() { + if (dirty) { + dependencies = ObservableTracker.runAndMonitor(new class() Runnable { + public void run() { + updateWidget(widget, element); + } + }, this, null); + dirty = false; + } + } + + // IChangeListener implementation (listening to any dependency) + public void handleChange(ChangeEvent event) { + // Whenever this updator becomes dirty, schedule the run() method + makeDirty(); + } + + protected final void makeDirty() { + if (!dirty) { + dirty = true; + stopListening(); + if (!theComposite.isDisposed()) { + SWTUtil.runOnce(theComposite.getDisplay(), this); + } + } + } + + private void stopListening() { + // Stop listening for dependency changes + for (int i = 0; i < dependencies.length; i++) { + IObservable observable = dependencies[i]; + + observable.removeChangeListener(this); + } + } + } + + private class LayoutRunnable : Runnable { + private bool posted = false; + private Set controlsToLayout = new HashSet(); + void add(Control toLayout) { + controlsToLayout.add(toLayout); + if (!posted) { + posted = true; + theComposite.getDisplay().asyncExec(this); + } + } + public void run() { + posted = false; + theComposite.getShell().layout(cast(Control[])controlsToLayout.toArray(new Control[controlsToLayout.size()])); + controlsToLayout.clear(); + } + } + + private LayoutRunnable layoutRunnable = new LayoutRunnable(); + + /** + * To be called from {@link #updateWidget(Widget, Object)} or {@link #createWidget(int)} + * if this updater's composite's layout may need to be updated. + * @param control + * @since 1.2 + */ + protected void requestLayout(Control control) { + layoutRunnable.add(control); + } + + private class PrivateInterface : DisposeListener, + IListChangeListener { + + // DisposeListener implementation + public void widgetDisposed(DisposeEvent e) { + this.outer.dispose(); + } + + public void handleListChange(ListChangeEvent event) { + ListDiffEntry[] diffs = event.diff.getDifferences(); + for (int i = 0; i < diffs.length; i++) { + ListDiffEntry listDiffEntry = diffs[i]; + if (listDiffEntry.isAddition()) { + createChild(listDiffEntry.getElement(), listDiffEntry.getPosition()); + } else { + disposeWidget(listDiffEntry.getPosition()); + } + } + theComposite.layout(); + } + + } + + private PrivateInterface privateInterface = new PrivateInterface(); + + private Composite theComposite; + + private IObservableList model; + + /** + * Creates an updater for the given control and list. For each element of + * the list, a child widget of the composite will be created using + * {@link #createWidget(int)}. + * + * @param toUpdate + * composite to update + * @param model + * an observable list to track + */ + public this(Composite toUpdate, IObservableList model) { + this.theComposite = toUpdate; + this.model = model; + + model.addListChangeListener(privateInterface); + theComposite.addDisposeListener(privateInterface); + ObservableTracker.runAndIgnore(new class() Runnable{ + public void run() { + int index = 0; + for (Iterator it = this.outer.model.iterator(); it.hasNext();) { + Object element = it.next(); + createChild(element, index++); + } + } + }); + } + + /** + * @param position + * @since 1.2 + */ + protected void disposeWidget(int position) { + theComposite.getChildren()[position].dispose(); + } + + /** + * This is called automatically when the control is disposed. It may also be + * called explicitly to remove this updator from the control. Subclasses + * will normally extend this method to detach any listeners they attached in + * their constructor. + */ + public void dispose() { + theComposite.removeDisposeListener(privateInterface); + model.removeListChangeListener(privateInterface); + } + + /** + * Creates a new child widget for the target composite at the given index. + * + *

    + * Subclasses should implement this method to provide the code that creates + * a child widget at a specific index. Note that + * {@link #updateWidget(Widget, Object)} will be called after this method + * returns. Only those properties of the widget that don't change over time + * should be set in this method. + *

    + * + * @param index + * the at which to create the widget + * @return the widget + */ + protected abstract Widget createWidget(int index); + + /** + * Updates the given widget based on the element found in the model list. + * This method will be invoked once after the widget is created, and once + * before any repaint during which the control is visible and dirty. + * + *

    + * Subclasses should implement this method to provide any code that changes + * the appearance of the widget. + *

    + * + * @param widget + * the widget to update + * @param element + * the element associated with the widget + */ + protected abstract void updateWidget(Widget widget, Object element); + + void createChild(Object element, int index) { + Widget newChild = createWidget(index); + final UpdateRunnable updateRunnable = new UpdateRunnable(newChild, element); + newChild.setData(updateRunnable); + updateRunnable.updateIfNecessary(); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/ControlUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/ControlUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2005, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.swt.ControlUpdater; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.widgets.Control; + +/** + * NON-API - A ControlUpdater updates an SWT control in response to changes in the model. + * By wrapping a block of code in a ControlUpdater, clients can rely on the fact + * that the block of code will be re-executed whenever anything changes in the + * model that might affect its behavior. + * + *

    + * ControlUpdaters only execute when their controls are visible. If something changes + * in the model while the control is invisible, the updator is flagged as dirty and + * the updator stops listening to the model until the next time the control repaints. + * This saves CPU cycles by deferring UI updates to widgets that are currently invisible. + *

    + * + *

    + * Clients should subclass this when copying information from the model to + * a control. Typical usage: + *

    + * + *
      + *
    • Override updateControl. It should do whatever is necessary to display + * the contents of the model in the control.
    • + *
    • In the constructor, attach listeners to the model. The listeners should + * call markDirty whenever anything changes in the model that affects + * updateControl. Note: this step can be omitted when calling any method + * tagged with "@TrackedGetter" since ControlUpdater will automatically attach + * a listener to any object if a "@TrackedGetter" method is called in + * updateControl.
    • + *
    • (optional)Extend dispose() to remove any listeners attached in the constructor
    • + *
    + * + *

    + * Example: + *

    + * + * + * // Displays an observable value in a label and keeps the label in synch with changes + * // in the value. + * IReadableValue someValue = ... + * final Label myLabel = new Label(parent, SWT.NONE); + * new ControlUpdater(myLabel) { + * protected void updateControl() { + * myLabel.setText(someValue.getValue().toString); + * } + * } + * // myLabel will display the value of someValue the next time it repaints, and will automatically + * // be updated whenever someValue changes and the label is visible + * + * + * @since 1.1 + */ +public abstract class ControlUpdater { + + private class PrivateInterface : PaintListener, + DisposeListener, Runnable, IChangeListener { + + // PaintListener implementation + public void paintControl(PaintEvent e) { + updateIfNecessary(); + } + + // DisposeListener implementation + public void widgetDisposed(DisposeEvent e) { + this.outer.dispose(); + } + + // Runnable implementation. This method runs at most once per repaint whenever the + // value gets marked as dirty. + public void run() { + if (theControl !is null && !theControl.isDisposed() && theControl.isVisible()) { + updateIfNecessary(); + } + } + + // IChangeListener implementation (listening to the ComputedValue) + public void handleChange(ChangeEvent event) { + // Whenever this updator becomes dirty, schedule the run() method + makeDirty(); + } + + } + + private Runnable updateRunnable = new class() Runnable { + public void run() { + updateControl(); + } + }; + + private PrivateInterface privateInterface = new PrivateInterface(); + private Control theControl; + private IObservable[] dependencies = new IObservable[0]; + private bool dirty = false; + + /** + * Creates an updater for the given control. + * + * @param toUpdate control to update + */ + public this(Control toUpdate) { + theControl = toUpdate; + + theControl.addDisposeListener(privateInterface); + theControl.addPaintListener(privateInterface); + makeDirty(); + } + + private void updateIfNecessary() { + if (dirty) { + dependencies = ObservableTracker.runAndMonitor(updateRunnable, privateInterface, null); + dirty = false; + } + } + + /** + * This is called automatically when the control is disposed. It may also + * be called explicitly to remove this updator from the control. Subclasses + * will normally extend this method to detach any listeners they attached + * in their constructor. + */ + public void dispose() { + theControl.removeDisposeListener(privateInterface); + theControl.removePaintListener(privateInterface); + + stopListening(); + } + + private void stopListening() { + // Stop listening for dependency changes + for (int i = 0; i < dependencies.length; i++) { + IObservable observable = dependencies[i]; + + observable.removeChangeListener(privateInterface); + } + } + + /** + * Updates the control. This method will be invoked once after the + * updator is created, and once before any repaint during which the + * control is visible and dirty. + * + *

    + * Subclasses should overload this method to provide any code that + * changes the appearance of the widget. + *

    + */ + protected abstract void updateControl(); + + /** + * Marks this updator as dirty. Causes the updateControl method to + * be invoked before the next time the control is repainted. + */ + protected final void makeDirty() { + if (!dirty) { + dirty = true; + stopListening(); + SWTUtil.runOnce(theControl.getDisplay(), privateInterface); + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/MenuUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/MenuUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,168 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.swt.MenuUpdater; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.MenuEvent; +import org.eclipse.swt.events.MenuListener; +import org.eclipse.swt.widgets.Menu; + +/** + * NON-API - A MenuUpdater updates an SWT menu in response to changes in the model. By + * wrapping a block of code in a MenuUpdater, clients can rely on the fact that + * the block of code will be re-executed whenever anything changes in the model + * that might affect its behavior. + * + *

    + * MenuUpdaters only execute once their menus are shown. If something changes in + * the model, the updater is flagged as dirty and it stops listening to the + * model until the next time the menu is shown. If the menu is visible while the + * model changes, it will be updated right away. + *

    + * + *

    + * Clients should subclass this when copying information from the model to a + * menu. Typical usage: + *

    + * + *
      + *
    • Override updateMenu. It should do whatever is necessary to display the + * contents of the model in the menu.
    • + *
    • In the constructor, attach listeners to the model. The listeners should + * call markDirty whenever anything changes in the model that affects + * updateMenu. Note: this step can be omitted when calling any method tagged + * with "@TrackedGetter" since MenuUpdater will automatically attach a listener + * to any object if a "@TrackedGetter" method is called in updateMenu.
    • + *
    • (optional)Extend dispose() to remove any listeners attached in the + * constructor
    • + *
    + * + * @since 1.1 + */ +public abstract class MenuUpdater { + + private class PrivateInterface : MenuListener, + DisposeListener, Runnable, IChangeListener { + + // DisposeListener implementation + public void widgetDisposed(DisposeEvent e) { + this.outer.dispose(); + } + + // Runnable implementation. This method runs at most once per repaint whenever the + // value gets marked as dirty. + public void run() { + if (theMenu !is null && !theMenu.isDisposed() && theMenu.isVisible()) { + updateIfNecessary(); + } + } + + // IChangeListener implementation (listening to the ComputedValue) + public void handleChange(ChangeEvent event) { + // Whenever this updator becomes dirty, schedule the run() method + makeDirty(); + } + + public void menuHidden(MenuEvent e) { + // do nothing + } + + public void menuShown(MenuEvent e) { + updateIfNecessary(); + } + + } + + private Runnable updateRunnable = new class() Runnable { + public void run() { + updateMenu(); + } + }; + + private PrivateInterface privateInterface = new PrivateInterface(); + private Menu theMenu; + private IObservable[] dependencies = new IObservable[0]; + private bool dirty = false; + + /** + * Creates an updator for the given menu. + * + * @param toUpdate menu to update + */ + public this(Menu toUpdate) { + theMenu = toUpdate; + + theMenu.addDisposeListener(privateInterface); + theMenu.addMenuListener(privateInterface); + makeDirty(); + } + + private void updateIfNecessary() { + if (dirty) { + dependencies = ObservableTracker.runAndMonitor(updateRunnable, privateInterface, null); + dirty = false; + } + } + + /** + * This is called automatically when the menu is disposed. It may also + * be called explicitly to remove this updator from the menu. Subclasses + * will normally extend this method to detach any listeners they attached + * in their constructor. + */ + public void dispose() { + theMenu.removeDisposeListener(privateInterface); + theMenu.removeMenuListener(privateInterface); + + stopListening(); + } + + private void stopListening() { + // Stop listening for dependency changes + for (int i = 0; i < dependencies.length; i++) { + IObservable observable = dependencies[i]; + + observable.removeChangeListener(privateInterface); + } + } + + /** + * Updates the menu. This method will be invoked once after the + * updater is created, and once for any SWT.Show event if this + * updater is marked as dirty at that time. + * + *

    + * Subclasses should overload this method to provide any code that + * udates the menu. + *

    + */ + protected abstract void updateMenu(); + + /** + * Marks this updator as dirty. Causes the updateControl method to + * be invoked before the next time the control is repainted. + */ + protected final void makeDirty() { + if (!dirty) { + dirty = true; + stopListening(); + SWTUtil.runOnce(theMenu.getDisplay(), privateInterface); + } + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/SWTUtil.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/SWTUtil.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.swt.SWTUtil; + +import java.lang.all; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.util.SafeRunnable; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; + +/** + * NON-API - Utility methods, mainly having to do with posting runnables to the UI thread + * in a particular way. + * @since 1.1 + * + */ +public class SWTUtil { + /** + * Stores a work queue for each display + */ + private static Map mapDisplayOntoWorkQueue = new HashMap(); + + private this() { + } + + /** + * Runs the given runnable on the given display as soon as possible. If + * possible, the runnable will be executed before the next widget is + * repainted, but this behavior is not guaranteed. Use this method to + * schedule work will affect the way one or more widgets are drawn. + * + *

    + * This is threadsafe. + *

    + * + * @param d + * display + * @param r + * runnable to execute in the UI thread. + */ + public static void greedyExec(Display d, Runnable r) { + if (d.isDisposed()) { + return; + } + + // if (Display.getCurrent() is d) { + // r.run(); + // } else { + WorkQueue queue = getQueueFor(d); + queue.asyncExec(r); + // } + } + + /** + * Runs the given runnable on the given display as soon as possible. Unlike + * greedyExec, this has no effect if the given runnable has already been + * scheduled for execution. Use this method to schedule work that will + * affect the way one or more wigdets are drawn, but that should only happen + * once. + * + *

    + * This is threadsafe. + *

    + * + * @param d + * display + * @param r + * runnable to execute in the UI thread. Has no effect if the + * given runnable has already been scheduled but has not yet run. + */ + public static void runOnce(Display d, Runnable r) { + if (d.isDisposed()) { + return; + } + WorkQueue queue = getQueueFor(d); + queue.runOnce(r); + } + + /** + * Cancels a greedyExec or runOnce that was previously scheduled on the + * given display. Has no effect if the given runnable is not in the queue + * for the given display + * + * @param d + * target display + * @param r + * runnable to execute + */ + public static void cancelExec(Display d, Runnable r) { + if (d.isDisposed()) { + return; + } + WorkQueue queue = getQueueFor(d); + queue.cancelExec(r); + } + + /** + * Returns the work queue for the given display. Creates a work queue if + * none exists yet. + * + * @param d + * display to return queue for + * @return a work queue (never null) + */ + private static WorkQueue getQueueFor(Display d) { + WorkQueue result; + synchronized (mapDisplayOntoWorkQueue) { + // Look for existing queue + result = cast(WorkQueue) mapDisplayOntoWorkQueue.get(d); + + if (result is null) { + // If none, create new queue + result = new WorkQueue(d); + final WorkQueue q = result; + mapDisplayOntoWorkQueue.put(d, result); + d.asyncExec(dgRunnable( (Display d_) { + d_.disposeExec(new class() Runnable { + public void run() { + synchronized (mapDisplayOntoWorkQueue) { + q.cancelAll(); + mapDisplayOntoWorkQueue.remove(d_); + } + } + }); + }, d)); + } + return result; + } + } + + /** + * @param rgb1 + * @param rgb2 + * @param ratio + * @return the RGB object + */ + public static RGB mix(RGB rgb1, RGB rgb2, double ratio) { + return new RGB(interp(rgb1.red, rgb2.red, ratio), + interp(rgb1.green, rgb2.green, ratio), + interp(rgb1.blue, rgb2.blue, ratio)); + } + + private static int interp(int i1, int i2, double ratio) { + int result = cast(int)(i1 * ratio + i2 * (1.0 - ratio)); + if (result < 0) result = 0; + if (result > 255) result = 255; + return result; + } + + /** + * Logs an exception as though it was thrown by a SafeRunnable being run + * with the default ISafeRunnableRunner. Will not open modal dialogs or spin + * the event loop. + * + * @param t + * throwable to log + * @deprecated + * @noreference This method is not intended to be referenced by clients. It + * remains here for API backwards compatibility. + */ + public static void logException(Exception t) { + SafeRunnable.run(new class(t) SafeRunnable { + Exception t_; + this(Exception a){ t_=a; } + public void run() { + throw t_; + } + public void handleException(Throwable e) { + // IMPORTANT: Do not call the super implementation, since + // it opens a modal dialog, and may cause *syncExecs to run + // too early. + } + }); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/TableUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/TableUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,222 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.swt.TableUpdater; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.ChangeEvent; +import org.eclipse.core.databinding.observable.IChangeListener; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.list.IListChangeListener; +import org.eclipse.core.databinding.observable.list.IObservableList; +import org.eclipse.core.databinding.observable.list.ListChangeEvent; +import org.eclipse.core.databinding.observable.list.ListDiffEntry; +import org.eclipse.core.runtime.Assert; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableItem; + +/** + * NON-API - This class can be used to update a table with automatic dependency + * tracking. + * + * @since 1.1 + * + * @noextend This class is not intended to be subclassed by clients. (We do + * encourage experimentation for non-production code and are + * interested in feedback though.) + * + */ +public abstract class TableUpdater { + + private class UpdateRunnable : Runnable, IChangeListener, + DisposeListener { + private TableItem item; + + private bool dirty = false; + + private IObservable[] dependencies = new IObservable[0]; + + private final Object element; + + this(TableItem item, Object element) { + this.item = item; + this.element = element; + item.addDisposeListener(this); + } + + // Runnable implementation. This method runs at most once per repaint + // whenever the + // value gets marked as dirty. + public void run() { + if (table !is null && !table.isDisposed() && item !is null + && !item.isDisposed()) { + if (table.isVisible()) { + int tableHeight = table.getClientArea().height; + int numVisibleItems = tableHeight / table.getItemHeight(); + int indexOfItem = table.indexOf(item); + int topIndex = table.getTopIndex(); + if (indexOfItem >= topIndex + && indexOfItem <= topIndex + numVisibleItems) { + updateIfNecessary(indexOfItem); + return; + } + } + table.clear(table.indexOf(item)); + } + } + + private void updateIfNecessary(int indexOfItem) { + if (dirty) { + dependencies = ObservableTracker.runAndMonitor(dgRunnable( (int indexOfItem_) { + updateItem(indexOfItem_, item, element); + }, indexOfItem), this, null); + dirty = false; + } + } + + // IChangeListener implementation (listening to the ComputedValue) + public void handleChange(ChangeEvent event) { + // Whenever this updator becomes dirty, schedule the run() method + makeDirty(); + } + + protected final void makeDirty() { + if (!dirty) { + dirty = true; + stopListening(); + SWTUtil.runOnce(table.getDisplay(), this); + } + } + + private void stopListening() { + // Stop listening for dependency changes + for (int i = 0; i < dependencies.length; i++) { + IObservable observable = dependencies[i]; + + observable.removeChangeListener(this); + } + } + + // DisposeListener implementation + public void widgetDisposed(DisposeEvent e) { + stopListening(); + dependencies = null; + item = null; + } + } + + private class PrivateInterface : Listener, DisposeListener { + + // Listener implementation + public void handleEvent(Event e) { + if (e.type is SWT.SetData) { + UpdateRunnable runnable = cast(UpdateRunnable) e.item.getData(); + if (runnable is null) { + runnable = new UpdateRunnable(cast(TableItem) e.item, list.get(e.index)); + e.item.setData(runnable); + runnable.makeDirty(); + } else { + runnable.updateIfNecessary(e.index); + } + } + } + + // DisposeListener implementation + public void widgetDisposed(DisposeEvent e) { + this.outer.dispose(); + } + + } + + private PrivateInterface privateInterface = new PrivateInterface(); + + private Table table; + + private IListChangeListener listChangeListener = new class() IListChangeListener { + public void handleListChange(ListChangeEvent event) { + ListDiffEntry[] differences = event.diff.getDifferences(); + for (int i = 0; i < differences.length; i++) { + ListDiffEntry entry = differences[i]; + if (entry.isAddition()) { + TableItem item = new TableItem(table, SWT.NONE, entry + .getPosition()); + UpdateRunnable updateRunnable = new UpdateRunnable(item, entry.getElement()); + item.setData(updateRunnable); + updateRunnable.makeDirty(); + } else { + table.getItem(entry.getPosition()).dispose(); + } + } + } + }; + + private IObservableList list; + + /** + * Creates an updator for the given control. + * + * @param table + * table to update + * @param list + * @since 1.2 + */ + public this(Table table, IObservableList list) { + this.table = table; + this.list = list; + Assert.isLegal((table.getStyle() & SWT.VIRTUAL) !is 0, + "TableUpdater requires virtual table"); //$NON-NLS-1$ + + table.setItemCount(list.size()); + list.addListChangeListener(listChangeListener); + + table.addDisposeListener(privateInterface); + table.addListener(SWT.SetData, privateInterface); + } + + /** + * This is called automatically when the control is disposed. It may also be + * called explicitly to remove this updator from the control. Subclasses + * will normally extend this method to detach any listeners they attached in + * their constructor. + */ + public void dispose() { + table.removeDisposeListener(privateInterface); + table.removeListener(SWT.SetData, privateInterface); + list.removeListChangeListener(listChangeListener); + table = null; + list = null; + } + + /** + * Updates the control. This method will be invoked once after the updator + * is created, and once before any repaint during which the control is + * visible and dirty. + * + *

    + * Subclasses should overload this method to provide any code that changes + * the appearance of the widget. + *

    + * + * @param index + * @param item + * the item to update + * @param element + * @since 1.2 + */ + protected abstract void updateItem(int index, TableItem item, Object element); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/WorkQueue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/WorkQueue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2006, 2007 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.swt.WorkQueue; + +import java.lang.all; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** + * NON-API - Helper class to manage a queue of runnables to be posted to the UI thread in a way + * that they are only run once. + * @since 1.1 + * + */ +public class WorkQueue { + + private bool updateScheduled = false; + + private bool paintListenerAttached = false; + + private LinkedList pendingWork = new LinkedList(); + + private Display d; + + private Set pendingWorkSet = new HashSet(); + + private Runnable updateJob = new class() Runnable { + public void run() { + doUpdate(); + updateScheduled = false; + } + }; + + private Listener paintListener = new class() Listener { + public void handleEvent(Event event) { + paintListenerAttached = false; + d.removeFilter(SWT.Paint, this); + doUpdate(); + } + }; + + /** + * @param targetDisplay + */ + public this(Display targetDisplay) { + d = targetDisplay; + } + + private void doUpdate() { + for (;;) { + Runnable next; + synchronized (pendingWork) { + if (pendingWork.isEmpty()) { + break; + } + next = cast(Runnable) pendingWork.removeFirst(); + pendingWorkSet.remove(next); + } + + next.run(); + } + } + + /** + * Schedules some work to happen in the UI thread as soon as possible. If + * possible, the work will happen before the next control redraws. The given + * runnable will only be run once. Has no effect if this runnable has + * already been queued for execution. + * + * @param work + * runnable to execute + */ + public void runOnce(Runnable work) { + synchronized (pendingWork) { + if (pendingWorkSet.contains(work)) { + return; + } + + pendingWorkSet.add(work); + + asyncExec(work); + } + } + + /** + * Schedules some work to happen in the UI thread as soon as possible. If + * possible, the work will happen before the next control redraws. Unlike + * runOnce, calling asyncExec twice with the same runnable will cause that + * runnable to run twice. + * + * @param work + * runnable to execute + */ + public void asyncExec(Runnable work) { + synchronized (pendingWork) { + pendingWork.add(work); + if (!updateScheduled) { + updateScheduled = true; + d.asyncExec(updateJob); + } + + // If we're in the UI thread, add an event filter to ensure + // the work happens ASAP + if (Display.getCurrent() is d) { + if (!paintListenerAttached) { + paintListenerAttached = true; + d.addFilter(SWT.Paint, paintListener); + } + } + } + } + + /** + * Cancels a previously-scheduled runnable. Has no effect if the given + * runnable was not previously scheduled or has already executed. + * + * @param toCancel + * runnable to cancel + */ + public void cancelExec(Runnable toCancel) { + synchronized (pendingWork) { + pendingWork.remove(toCancel); + pendingWorkSet.remove(toCancel); + } + } + + /** + * Cancels all pending work. + */ + public void cancelAll() { + synchronized (pendingWork) { + pendingWork.clear(); + pendingWorkSet.clear(); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/IParentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/IParentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Stefan Xenos, IBM - initial API and implementation + * Matthew Hall - bug 207858 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.viewers.IParentProvider; + +import java.lang.all; + +/** + * NON-API - Returns the parent of elements in a tree. + * + * @since 1.1 + */ +public interface IParentProvider { + + /** + * Returns the parent of the passed in child element, or null if unknown. + * + * @param child + * the child element + * @return the parent of the passed in child element, or null if unknown. + */ + public Object getParent(Object child); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/TreeNode.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/TreeNode.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,341 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Stefan Xenos, IBM - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.viewers.TreeNode; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.Observables; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; +import org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.widgets.Control; + +/** + * + * @since 1.0 + * @deprecated Use {@link ObservableSetTreeContentProvider} or + * {@link ObservableListTreeContentProvider} instead. + */ +/* package */ class TreeNode : ISetChangeListener, IStaleListener { + private UnorderedTreeContentProvider contentProvider; + private Object element; + + // Stores the set of parents (null if there are less than 2) + private HashSet parents = null; + + // Stores one representative parent. If there is more than one parent, + // the complete set of parents can be found in the parents set. + Object parent; + + /** + * Set of child elements. + */ + private IObservableSet children; + + private bool hasPendingNode = false; + private bool isStale; + private bool listeningToChildren = false; + private bool prefetchEnqueued = false; + + /** + * @param element + * @param cp + */ + public this(Object element, UnorderedTreeContentProvider cp) { + this.element = element; + this.contentProvider = cp; + children = contentProvider.createChildSet(element); + if (children is null) { + children = Observables.emptyObservableSet(); + listeningToChildren = true; + } + hasPendingNode = children.isStale(); + } + + /** + * @param parent + */ + public void addParent(Object parent) { + if (this.parent is null) { + this.parent = parent; + } else { + if (parent.equals(this.parent)) { + return; + } + if (parents is null) { + parents = new HashSet(); + parents.add(this.parent); + } + parents.add(parent); + } + } + + /** + * @param parent + */ + public void removeParent(Object parent) { + if (this.parents !is null) { + parents.remove(parent); + } + + if (parent is this.parent) { + if (parents is null || parents.isEmpty()) { + this.parent = null; + } else { + this.parent = parents.iterator().next(); + } + } + + if (this.parents !is null && this.parents.size() <= 1) { + this.parents = null; + } + } + + /** + * Returns the set of children for this node. If new children are discovered later, they + * will be added directly to the viewer. + * + * @return TODO + */ + public Set getChildren() { + if (!listeningToChildren) { + listeningToChildren = true; + children.addSetChangeListener(this); + hasPendingNode = children.isEmpty() && children.isStale(); + children.addStaleListener(this); + updateStale(); + } + + // If the child set is stale and empty, show the "pending" node + if (hasPendingNode) { + Object pendingNode = contentProvider.getPendingNode(); + return Collections.singleton(pendingNode); + } + return children; + } + + /** + * @return TODO + */ + public IObservableSet getChildrenSet() { + return children; + } + + private void updateStale() { + bool willBeStale = children.isStale(); + if (willBeStale !is isStale) { + isStale = willBeStale; + + contentProvider.changeStale(isStale? 1 : -1); + } + } + + /** + * @return TODO + */ + public bool isStale() { + return isStale; + } + + /** + * Returns true if the viewer should show a plus sign for expanding this + * node. + * + * @return TODO + */ + public bool shouldShowPlus() { + if (children is null) { +// if (!hasPendingNode) { +// hasPendingNode = true; +// contentProvider.add(element, Collections.singleton(contentProvider.getPendingNode())); +// } + return true; + } + if (!listeningToChildren && !prefetchEnqueued) { + prefetchEnqueued = true; + contentProvider.enqueuePrefetch(this); + } + return !listeningToChildren || hasPendingNode || !children.isEmpty(); + } + + /** + * Disposes this node and removes all remaining children. + */ + public void dispose() { + if (children !is null) { + if (listeningToChildren) { + contentProvider.remove(element, children, true); + children.removeSetChangeListener(this); + children.removeStaleListener(this); + } + children.dispose(); + children = null; + + if (listeningToChildren && isStale) { + contentProvider.changeStale(-1); + } + } + } + + /** + * @return TODO + */ + public bool isDisposed() { + return children is null; + } + + /** + * Returns one representative parent, or null if this node is unparented. Use + * getParents() to get the complete set of known parents. + * + * @return TODO + */ + public Object getParent() { + return parent; + } + + /** + * + * @return the set of all known parents for this node + */ + public Set getParents() { + if (parents is null) { + if (parent is null) { + return Collections.EMPTY_SET; + } + return Collections.singleton(parent); + } + return parents; + } + + /** + * Called when the child set changes. Should not be called directly by the viewer. + */ + public void handleSetChange(SetChangeEvent event) { + SetDiff diff = event.diff; + TreeViewer viewer = this.contentProvider.getViewer(); + if (viewer !is null) { + Control control = viewer.getControl(); + if (control !is null) { + if (control.isDisposed()) { + // If the widgetry was disposed without notifying the content provider, then + // dispose the content provider now and stop processing events. + contentProvider.dispose(); + return; + } + } + } + + bool shouldHavePendingNode = children.isEmpty() && children.isStale(); + + Set additions = diff.getAdditions(); + // Check if we should add the pending node + if (shouldHavePendingNode && !hasPendingNode) { + HashSet newAdditions = new HashSet(); + newAdditions.addAll(additions); + newAdditions.add(contentProvider.getPendingNode()); + additions = newAdditions; + hasPendingNode = true; + } + + Set removals = diff.getRemovals(); + // Check if we should remove the pending node + if (!shouldHavePendingNode && hasPendingNode) { + HashSet newRemovals = new HashSet(); + newRemovals.addAll(removals); + newRemovals.add(contentProvider.getPendingNode()); + removals = newRemovals; + hasPendingNode = false; + } + if (!additions.isEmpty()) { + contentProvider.add(element, additions); + } + if (!removals.isEmpty()) { + contentProvider.remove(element, removals, children.isEmpty() && !hasPendingNode); + } + + updateStale(); + } + + public void handleStale(StaleEvent staleEvent) { + TreeViewer viewer = this.contentProvider.getViewer(); + if (viewer !is null) { + Control control = viewer.getControl(); + if (control !is null) { + if (control.isDisposed()) { + // If the widgetry was disposed without notifying the content provider, then + // dispose the content provider now and stop processing events. + contentProvider.dispose(); + return; + } + } + } + + bool shouldHavePendingNode = children.isEmpty() && children.isStale(); + + // Check if we should add the pending node + if (shouldHavePendingNode && !hasPendingNode) { + hasPendingNode = shouldHavePendingNode; + contentProvider.add(element, Collections.singleton(contentProvider.getPendingNode())); + } + + // Check if we should remove the pending node + if (!shouldHavePendingNode && hasPendingNode) { + hasPendingNode = shouldHavePendingNode; + contentProvider.remove(element, Collections.singleton(contentProvider.getPendingNode()), true); + } + + updateStale(); + } + + /** + * @return TODO + */ + public Object getElement() { + return element; + } + + /** + * + */ + public void prefetch() { + TreeViewer viewer = this.contentProvider.getViewer(); + if (viewer !is null) { + Control control = viewer.getControl(); + if (control !is null) { + if (control.isDisposed()) { + // If the widgetry has been disposed, then avoid sending anything + // to the viewer. + return; + } + } + } + + Set children = getChildren(); + if (!children.isEmpty()) { + contentProvider.add(element, children); + } else { + // We need to remove the + sign, and adding/removing elements won't do the trick + contentProvider.getViewer().refresh(element); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/UnorderedTreeContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/UnorderedTreeContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,533 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Stefan Xenos, IBM - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.viewers.UnorderedTreeContentProvider; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.set.AbstractObservableSet; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider; +import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider; +import org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.ITreePathContentProvider; +import org.eclipse.jface.viewers.ITreeViewerListener; +import org.eclipse.jface.viewers.TreeExpansionEvent; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; + +/** + * NON-API - Generic tree content provider to be used with an AbstractTreeViewer based on a IUnorderedTreeProvider. + * @since 1.1 + * @deprecated Use {@link ObservableSetTreeContentProvider} or + * {@link ObservableListTreeContentProvider} instead. + */ +public class UnorderedTreeContentProvider : ITreeContentProvider, ITreePathContentProvider { + + private HashMap mapElementToTreeNode = new HashMap(); + private LinkedList enqueuedPrefetches = new LinkedList(); + private IParentProvider rootParentProvider = null; + private bool useTreePaths = false; + + class KnownElementsSet : AbstractObservableSet { + + protected this() { + super(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.internal.databinding.provisional.observable.set.AbstractObservableSet#getWrappedSet() + */ + protected Set getWrappedSet() { + return mapElementToTreeNode.keySet(); + } + + void doFireDiff(Set added, Set removed) { + fireSetChange(Diffs.createSetDiff(added, removed)); + } + + public void fireSetChange(SetDiff diff) { + super.fireSetChange(diff); + } + + void doFireStale(bool isStale) { + if (isStale) { + fireStale(); + } else { + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, Collections.EMPTY_SET)); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.internal.databinding.provisional.observable.set.IObservableSet#getElementType() + */ + public Object getElementType() { + return new Object(); + } + } + + KnownElementsSet elements = new KnownElementsSet(); + + private ITreeViewerListener expandListener = new class() ITreeViewerListener { + public void treeCollapsed(TreeExpansionEvent event) { + } + + public void treeExpanded(TreeExpansionEvent event) { + } + }; + + private IUnorderedTreeProvider provider; + private Object pendingNode; + + private int avoidViewerUpdates; + + private TreeViewer treeViewer; + + private int staleCount = 0; + private bool useRefresh; + private int maxPrefetches = 0; + + /** + * Constructs a content provider that will render the given tree in a TreeViewer. + * + * @param provider IObservableTree that provides the contents of the tree + * @param pendingNode element to insert whenever a node is being fetched in the background + * @param useRefresh true = notify the viewer of changes by calling refresh(...), false = + * notify the viewer of changes by calling add(...) and remove(...). Using false + * is more efficient, but may not work with TreeViewer subclasses. + */ + public this(IUnorderedTreeProvider provider, + Object pendingNode, bool useRefresh) { + this.provider = provider; + this.pendingNode = pendingNode; + this.useRefresh = useRefresh; + } + + /** + * Sets whether this content provider should add/remove elements using + * TreePaths (true) or elements (false). + * + *

    + *

    When using elements:

    + * + *
      + *
    • Cycles are permitted (elements can be their own ancestor)
    • + *
    • Addition, removal, and refresh are slightly faster
    • + *
    • It is not possible to have more than one content provider per tree
    • + *
    • The setRootPath(...) method is ignored
    • + *
    + * + *

    + *

    When using TreePaths:

    + * + *
      + *
    • Cycles are not permitted (elements cannot be their own parent)
    • + *
    • Addition, removal, and refresh are slightly slower
    • + *
    • It is possible to use more than one content provider in the same tree
    • + *
    • The setRootPath(...) method can be used to direct the output to a particular + * subtree
    • + *
    + * + * @param usePaths + */ + public void useTreePaths(bool usePaths) { + this.useTreePaths = usePaths; + } + + /** + * @param rootParentProvider + */ + public void setRootPath(IParentProvider rootParentProvider) { + this.rootParentProvider = rootParentProvider; + } + + /** + * @param maxPrefetches + */ + public void setMaxPrefetches(int maxPrefetches) { + this.maxPrefetches = maxPrefetches; + } + + /* package */ IObservableSet createChildSet(Object element) { + return provider.createChildSet(element); + } + + /* package */ void remove(Object element, Set removals, bool lastElement) { + if (removals.isEmpty()) { + return; + } + if (avoidViewerUpdates is 0) { + if (lastElement || useRefresh) { + doRefresh(element); + } else { + if (useTreePaths) { + List toRemove = new ArrayList(); + TreePath[] parents = getParents(element); + for (int i = 0; i < parents.length; i++) { + TreePath parent = parents[i]; + + for (Iterator iter = removals.iterator(); iter.hasNext();) { + Object elementToRemove = iter.next(); + + toRemove.add(parent.createChildPath(element).createChildPath(elementToRemove)); + } + } + + treeViewer.remove(toRemove.toArray(new TreePath[toRemove.size()])); + } else { + treeViewer.remove(element, removals.toArray()); + } + } + for (Iterator iter = removals.iterator(); iter.hasNext();) { + Object next = iter.next(); + + TreeNode nextNode = cast(TreeNode)mapElementToTreeNode.get(next); + if (nextNode !is null) { + nextNode.removeParent(element); + removeIfUnused(nextNode); + } + } + } + } + + /* package */ void add(Object element, Set additions) { + if (additions.isEmpty()) { + return; + } + if (avoidViewerUpdates is 0) { + // Handle new parents + addParent(element, additions); + if (useRefresh) { + doRefresh(element); + } else { + if (useTreePaths) { + TreePath[] parents = getParents(element); + for (int i = 0; i < parents.length; i++) { + TreePath parent = parents[i]; + + treeViewer.add(parent.createChildPath(element), additions.toArray()); + } + } else { + treeViewer.add(element, additions.toArray()); + } + } + } + } + + private void doRefresh(Object element) { + treeViewer.refresh(element); + } + + /** + * Ensures that the given set of children have the given parent as + * one of their parents. + * + * @param parent + * @param children + */ + private void addParent(Object parent, Set children) { + for (Iterator iter = children.iterator(); iter.hasNext();) { + Object next = iter.next(); + + TreeNode nextNode = getNode(next); + nextNode.addParent(parent); + } + } + + /** + * @return saouesnth + */ + public final Object getPendingNode() { + return pendingNode; + } + + /** + * @param parent + * @return aueosnht + */ + public IObservableSet getChildrenSet(Object parent) { + IObservableSet result = getNode(parent).getChildrenSet(); + + return result; + } + + public void dispose() { + if (treeViewer !is null) { + try { + avoidViewerUpdates++; + enqueuedPrefetches.clear(); + Object[] keys = mapElementToTreeNode.keySet().toArray(); + + for (int i = 0; i < keys.length; i++) { + Object key = keys[i]; + + TreeNode result = cast(TreeNode)mapElementToTreeNode.get(key); + if (result !is null) { + result.dispose(); + } + } + setViewer(null); + } finally { + avoidViewerUpdates--; + } + } + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // This should only ever be called for a single viewer + setViewer(viewer); + + if (oldInput !is null && newInput !is null && oldInput.equals(newInput)) { + return; + } + + try { + avoidViewerUpdates++; + TreeNode oldNode = cast(TreeNode)mapElementToTreeNode.get(oldInput); + if (oldNode !is null) { + removeIfUnused(oldNode); + } + } finally { + avoidViewerUpdates--; + } + } + + private void removeIfUnused(TreeNode toRemove) { + //TreeNode result = cast(TreeNode)mapElementToTreeNode.get(element); + Object element = toRemove.getElement(); + if (toRemove.getParent() is null) { + mapElementToTreeNode.remove(element); + elements.doFireDiff(Collections.EMPTY_SET, Collections.singleton(element)); + toRemove.dispose(); + } + } + + private void setViewer(Viewer viewer) { + if (viewer !is null && !(null !is cast(TreeViewer)viewer)) { + throw new IllegalArgumentException("This content provider can only be used with TreeViewers"); //$NON-NLS-1$ + } + TreeViewer newTreeViewer = cast(TreeViewer) viewer; + + if (newTreeViewer !is treeViewer) { + if (treeViewer !is null) { + treeViewer.removeTreeListener(expandListener); + } + + this.treeViewer = newTreeViewer; + if (newTreeViewer !is null) { + newTreeViewer.addTreeListener(expandListener); + } + } + } + + public Object[] getChildren(Object parentElement) { + Set result = getNode(parentElement).getChildren(); + + addParent(parentElement, result); + + return result.toArray(); + } + + private TreeNode getNode(Object parentElement) { + TreeNode result = cast(TreeNode)mapElementToTreeNode.get(parentElement); + if (result is null) { + result = new TreeNode(parentElement, this); + mapElementToTreeNode.put(parentElement, result); + elements.fireSetChange(Diffs.createSetDiff(Collections.singleton(parentElement), + Collections.EMPTY_SET)); + } + return result; + } + + public Object getParent(Object element) { + Object result = getNode(element).getParent(); + if (result is null && rootParentProvider !is null) { + result = rootParentProvider.getParent(element); + } + return result; + } + + public bool hasChildren(Object element) { + return getNode(element).shouldShowPlus(); + } + + public Object[] getElements(Object inputElement) { + return getChildren(inputElement); + } + + /** + * @return aouesnth + */ + public IObservableSet getKnownElements() { + return elements; + } + + /* package */ void changeStale(int staleDelta) { + staleCount += staleDelta; + processPrefetches(); + elements.setStale(staleCount !is 0); + } + + /** + * @return aoueesnth + */ + public TreeViewer getViewer() { + return treeViewer; + } + + /** + * @param element + * @return aoeusnth + */ + public bool isDirty(Object element) { + return false; + } + + /* package */ void enqueuePrefetch(TreeNode node) { + if (maxPrefetches > 0 || maxPrefetches is -1) { + if (staleCount is 0) { + // Call node.getChildren()... this will cause us to start listening to the + // node and will trigger prefetching. Don't call prefetch since this method + // is intended to be called inside getters (which will simply return the + // fetched nodes) and prefetch() is intended to be called inside an asyncExec, + // which will notify the viewer directly of the newly discovered nodes. + node.getChildren(); + } else { + enqueuedPrefetches.add(node); + while (maxPrefetches >= 0 && enqueuedPrefetches.size() > maxPrefetches) { + enqueuedPrefetches.removeFirst(); + } + } + } + } + + private void processPrefetches() { + while (staleCount is 0 && !enqueuedPrefetches.isEmpty()) { + TreeNode next = cast(TreeNode)enqueuedPrefetches.removeLast(); + + // Note that we don't remove nodes from the prefetch queue when they are disposed, + // so we may encounter disposed nodes at this time. + if (!next.isDisposed()) { + next.prefetch(); + } + } + } + + public Object[] getChildren(TreePath parentPath) { + return getChildren(parentPath.getLastSegment()); + } + + public TreePath[] getParents(Object element) { + // Compute all paths that do not contain cycles + /** + * List of Lists + */ + List parentPaths = computeParents(element, new HashSet()); + + /** + * List of TreePath + */ + List result = new ArrayList(); + + for (Iterator iterator = parentPaths.iterator(); iterator.hasNext();) { + List nextPath = cast(List) iterator.next(); + + LinkedList resultPath = new LinkedList(); + resultPath.addAll(nextPath); + Object nextParent = resultPath.isEmpty() ? element : resultPath.getFirst(); + for(;nextParent !is null;) { + if (rootParentProvider !is null) { + nextParent = rootParentProvider.getParent(nextParent); + if (nextParent !is null) { + resultPath.addFirst(nextParent); + } + } else { + nextParent = null; + } + } + + result.add(new TreePath(resultPath.toArray())); + } + + if (result.isEmpty() && rootParentProvider !is null) { + Object nextParent = rootParentProvider.getParent(element); + if (nextParent !is null) { + LinkedList resultPath = new LinkedList(); + while (nextParent !is null) { + resultPath.addFirst(nextParent); + nextParent = rootParentProvider.getParent(nextParent); + } + + result.add(new TreePath(resultPath.toArray())); + } + + } + + return cast(TreePath[]) result.toArray(new TreePath[result.size()]); + } + + /** + * + * @param node + * @param toIgnore + * @return a list of Lists, indicating all known paths to the given node + */ + private List computeParents(Object node, HashSet toIgnore) { + List result = new ArrayList(); + bool containedNode = toIgnore.add(node); + + TreeNode tn = getNode(node); + + HashSet parents = new HashSet(); + parents.addAll(tn.getParents()); + parents.removeAll(toIgnore); + if (parents.isEmpty()) { + ArrayList newPath = new ArrayList(); + result.add(newPath); + } else { + for (Iterator iterator = parents.iterator(); iterator.hasNext();) { + Object parent = iterator.next(); + + List parentPaths = computeParents(parent, toIgnore); + + for (Iterator iterator2 = parentPaths.iterator(); iterator2 + .hasNext();) { + List parentPath = cast(List) iterator2.next(); + + parentPath.add(parent); + result.add(parentPath); + } + } + } + + if (containedNode) { + toIgnore.remove(node); + } + return result; + } + + public bool hasChildren(TreePath path) { + return hasChildren(path.getLastSegment()); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/ViewerLabelProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/ViewerLabelProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 223123 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.provisional.viewers.ViewerLabelProvider; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.databinding.util.Policy; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ILabelProviderListener; +import org.eclipse.jface.viewers.IViewerLabelProvider; +import org.eclipse.jface.viewers.LabelProviderChangedEvent; +import org.eclipse.jface.viewers.ViewerLabel; +import org.eclipse.swt.graphics.Image; + +/** + * NON-API - Generic viewer label provider. + * @since 1.1 + * + */ +public class ViewerLabelProvider : IViewerLabelProvider, + ILabelProvider { + + private List listeners = new ArrayList(); + + /** + * Subclasses should override this method. They should not call the base + * class implementation. + */ + public void updateLabel(ViewerLabel label, Object element) { + label.setText(element.toString()); + } + + protected final void fireChangeEvent(Collection changes) { + final LabelProviderChangedEvent event = new LabelProviderChangedEvent( + this, changes.toArray()); + ILabelProviderListener[] listenerArray = cast(ILabelProviderListener[]) listeners + .toArray(new ILabelProviderListener[listeners.size()]); + for (int i = 0; i < listenerArray.length; i++) { + ILabelProviderListener listener = listenerArray[i]; + try { + listener.labelProviderChanged(event); + } catch (Exception e) { + Policy.getLog().log( + new Status(IStatus.ERROR, Policy.JFACE_DATABINDING, e + .getLocalizedMessage(), e)); + } + } + } + + public final Image getImage(Object element) { + ViewerLabel label = new ViewerLabel("", null); //$NON-NLS-1$ + updateLabel(label, element); + return label.getImage(); + } + + public final String getText(Object element) { + ViewerLabel label = new ViewerLabel("", null); //$NON-NLS-1$ + updateLabel(label, element); + return label.getText(); + } + + public void addListener(ILabelProviderListener listener) { + listeners.add(listener); + } + + public void dispose() { + listeners.clear(); + } + + public final bool isLabelProperty(Object element, String property) { + return true; + } + + public void removeListener(ILabelProviderListener listener) { + listeners.remove(listener); + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ButtonObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; + +/** + * @since 1.0 + * + */ +public class ButtonObservableValue : AbstractSWTObservableValue { + + private final Button button; + + private bool selectionValue; + + private Listener updateListener = new class() Listener { + public void handleEvent(Event event) { + bool oldSelectionValue = selectionValue; + selectionValue = button.getSelection(); + + notifyIfChanged(oldSelectionValue, selectionValue); + } + }; + + /** + * @param button + */ + public this(Button button) { + super(button); + this.button = button; + init(); + } + + /** + * @param realm + * @param button + */ + public this(Realm realm, Button button) { + super(realm, button); + this.button = button; + init(); + } + + private void init() { + button.addListener(SWT.Selection, updateListener); + button.addListener(SWT.DefaultSelection, updateListener); + } + + public void doSetValue(Object value) { + bool oldSelectionValue = selectionValue; + selectionValue = value is null ? false : (cast(Boolean) value) + .booleanValue(); + + button.setSelection(selectionValue); + notifyIfChanged(oldSelectionValue, selectionValue); + } + + public Object doGetValue() { + return button.getSelection() ? Boolean.TRUE : Boolean.FALSE; + } + + public Object getValueType() { + return Boolean.TYPE; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + + if (!button.isDisposed()) { + button.removeListener(SWT.Selection, updateListener); + button.removeListener(SWT.DefaultSelection, updateListener); + } + } + + /** + * Notifies consumers with a value change event only if a change occurred. + * + * @param oldValue + * @param newValue + */ + private void notifyIfChanged(bool oldValue, bool newValue) { + if (oldValue !is newValue) { + fireValueChange(Diffs.createValueDiff(oldValue ? Boolean.TRUE : Boolean.FALSE, + newValue ? Boolean.TRUE : Boolean.FALSE)); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.CComboObservableList; + +import java.lang.all; + +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.swt.custom.CCombo; + +/** + * @since 3.2 + * + */ +public class CComboObservableList : SWTObservableList { + + private final CCombo ccombo; + + /** + * @param ccombo + */ + public this(CCombo ccombo) { + super(SWTObservables.getRealm(ccombo.getDisplay())); + this.ccombo = ccombo; + } + + protected int getItemCount() { + return ccombo.getItemCount(); + } + + protected void setItems(String[] newItems) { + ccombo.setItems(newItems); + } + + protected String[] getItems() { + return ccombo.getItems(); + } + + protected String getItem(int index) { + return ccombo.getItem(index); + } + + protected void setItem(int index, String string) { + ccombo.setItem(index, string); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,168 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + * Matthew Hall - bug 118516 + * Eric Rizzo - bug 134884 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.CComboObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; + +/** + * @since 3.2 + * + */ +public class CComboObservableValue : AbstractSWTObservableValue { + + /** + * + */ + + private final CCombo ccombo; + + private final String attribute; + + private bool updating = false; + + private String currentValue; + + private ModifyListener modifyListener; + + /** + * @param ccombo + * @param attribute + */ + public this(CCombo ccombo, String attribute) { + super(ccombo); + this.ccombo = ccombo; + this.attribute = attribute; + init(); + } + + /** + * @param realm + * @param ccombo + * @param attribute + */ + public this(Realm realm, CCombo ccombo, String attribute) { + super(realm, ccombo); + this.ccombo = ccombo; + this.attribute = attribute; + init(); + } + + private void init() { + if (attribute.equals(SWTProperties.SELECTION) + || attribute.equals(SWTProperties.TEXT)) { + this.currentValue = ccombo.getText(); + modifyListener = new class() ModifyListener { + + public void modifyText(ModifyEvent e) { + if (!updating) { + String oldValue = currentValue; + currentValue = this.outer.ccombo + .getText(); + + notifyIfChanged(oldValue, currentValue); + } + } + }; + ccombo.addModifyListener(modifyListener); + } else + throw new IllegalArgumentException(); + } + + public void doSetValue(Object value) { + String oldValue = ccombo.getText(); + try { + updating = true; + if (attribute.equals(SWTProperties.TEXT)) { + String stringValue = value !is null ? value.toString() : ""; //$NON-NLS-1$ + ccombo.setText(stringValue); + } else if (attribute.equals(SWTProperties.SELECTION)) { + String items[] = ccombo.getItems(); + int index = -1; + if (value is null) { + ccombo.select(-1); + } else if (items !is null) { + for (int i = 0; i < items.length; i++) { + if (value.equals(items[i])) { + index = i; + break; + } + } + if (index is -1) { + ccombo.setText(cast(String) value); + } else { + ccombo.select(index); // -1 will not "unselect" + } + } + } + } finally { + updating = false; + currentValue = ccombo.getText(); + } + + notifyIfChanged(oldValue, currentValue); + } + + public Object doGetValue() { + if (attribute.equals(SWTProperties.TEXT)) + return ccombo.getText(); + + Assert.isTrue(attribute.equals(SWTProperties.SELECTION), + "unexpected attribute: " + attribute); //$NON-NLS-1$ + // The problem with a ccombo, is that it changes the text and + // fires before it update its selection index + return ccombo.getText(); + } + + public Object getValueType() { + Assert.isTrue(attribute.equals(SWTProperties.TEXT) + || attribute.equals(SWTProperties.SELECTION), + "unexpected attribute: " + attribute); //$NON-NLS-1$ + return String.classinfo; + } + + /** + * @return attribute being observed + */ + public String getAttribute() { + return attribute; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + + if (modifyListener !is null && !ccombo.isDisposed()) { + ccombo.removeModifyListener(modifyListener); + } + } + + private void notifyIfChanged(String oldValue, String newValue) { + if (!oldValue.equals(newValue)) { + fireValueChange(Diffs.createValueDiff(oldValue, ccombo.getText())); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSingleSelectionObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSingleSelectionObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.CComboSingleSelectionObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.swt.custom.CCombo; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; + +/** + * @since 1.0 + * + */ +public class CComboSingleSelectionObservableValue : + SingleSelectionObservableValue { + + private SelectionListener selectionListener; + + /** + * @param combo + */ + public this(CCombo combo) { + super(combo); + } + + /** + * @param realm + * @param combo + */ + public this(Realm realm, CCombo combo) { + super(realm, combo); + } + + private CCombo getCCombo() { + return cast(CCombo) getWidget(); + } + + protected void doAddSelectionListener(Runnable runnable) { + selectionListener = new class(runnable) SelectionListener { + Runnable runnable_; + this(Runnable r ){ runnable_=r; } + public void widgetDefaultSelected(SelectionEvent e) { + runnable_.run(); + } + + public void widgetSelected(SelectionEvent e) { + runnable_.run(); + } + }; + getCCombo().addSelectionListener(selectionListener); + } + + protected int doGetSelectionIndex() { + return getCCombo().getSelectionIndex(); + } + + protected void doSetSelectionIndex(int index) { + getCCombo().setText(getCCombo().getItem(index)); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + if (selectionListener !is null && !getCCombo().isDisposed()) { + getCCombo().removeSelectionListener(selectionListener); + } + + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.CLabelObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.custom.CLabel; + +/** + * @since 1.0 + * + */ +public class CLabelObservableValue : AbstractSWTObservableValue { + + private final CLabel label; + + /** + * @param label + */ + public this(CLabel label) { + super(label); + this.label = label; + } + + /** + * @param realm + * @param label + */ + public this(Realm realm, CLabel label) { + super(realm, label); + this.label = label; + } + + public void doSetValue(Object value) { + String oldValue = label.getText(); + String newValue = value is null ? "" : value.toString(); //$NON-NLS-1$ + label.setText(newValue); + + if (!newValue.equals(oldValue)) { + fireValueChange(Diffs.createValueDiff(oldValue, newValue)); + } + } + + public Object doGetValue() { + return label.getText(); + } + + public Object getValueType() { + return String.classinfo; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ComboObservableList; + +import java.lang.all; + +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.swt.widgets.Combo; + +/** + * @since 3.2 + * + */ +public class ComboObservableList : SWTObservableList { + + private final Combo combo; + + /** + * @param combo + */ + public this(Combo combo) { + super(SWTObservables.getRealm(combo.getDisplay())); + this.combo = combo; + } + + protected int getItemCount() { + return combo.getItemCount(); + } + + protected void setItems(String[] newItems) { + combo.setItems(newItems); + } + + protected String[] getItems() { + return combo.getItems(); + } + + protected String getItem(int index) { + return combo.getItem(index); + } + + protected void setItem(int index, String string) { + combo.setItem(index, string); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + * Matthew Hall - bug 118516 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ComboObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.widgets.Combo; + +/** + * @since 3.2 + * + */ +public class ComboObservableValue : AbstractSWTObservableValue { + + private final Combo combo; + private final String attribute; + private bool updating = false; + private String currentValue; + private ModifyListener modifyListener; + + /** + * @param combo + * @param attribute + */ + public this(Combo combo, String attribute) { + super(combo); + this.combo = combo; + this.attribute = attribute; + init(); + } + + /** + * @param realm + * @param combo + * @param attribute + */ + public this(Realm realm, Combo combo, String attribute) { + super(realm, combo); + this.combo = combo; + this.attribute = attribute; + init(); + } + + private void init() { + if (attribute.equals(SWTProperties.SELECTION) + || attribute.equals(SWTProperties.TEXT)) { + this.currentValue = combo.getText(); + modifyListener = new class() ModifyListener { + + public void modifyText(ModifyEvent e) { + if (!updating) { + String oldValue = currentValue; + currentValue = this.outer.combo + .getText(); + + notifyIfChanged(oldValue, currentValue); + } + } + }; + combo.addModifyListener(modifyListener); + } else + throw new IllegalArgumentException(); + } + + public void doSetValue(Object value) { + String oldValue = combo.getText(); + try { + updating = true; + if (attribute.equals(SWTProperties.TEXT)) { + String stringValue = value !is null ? value.toString() : ""; //$NON-NLS-1$ + combo.setText(stringValue); + } else if (attribute.equals(SWTProperties.SELECTION)) { + String items[] = combo.getItems(); + int index = -1; + if (items !is null && value !is null) { + for (int i = 0; i < items.length; i++) { + if (value.equals(items[i])) { + index = i; + break; + } + } + if (index is -1) { + combo.setText(cast(String) value); + } else { + combo.select(index); // -1 will not "unselect" + } + } + } + } finally { + updating = false; + currentValue = combo.getText(); + } + + notifyIfChanged(oldValue, currentValue); + } + + public Object doGetValue() { + if (attribute.equals(SWTProperties.TEXT)) + return combo.getText(); + + Assert.isTrue(attribute.equals(SWTProperties.SELECTION), + "unexpected attribute: " + attribute); //$NON-NLS-1$ + // The problem with a ccombo, is that it changes the text and + // fires before it update its selection index + return combo.getText(); + } + + public Object getValueType() { + Assert.isTrue(attribute.equals(SWTProperties.TEXT) + || attribute.equals(SWTProperties.SELECTION), + "unexpected attribute: " + attribute); //$NON-NLS-1$ + return String.classinfo; + } + + /** + * @return attribute being observed + */ + public String getAttribute() { + return attribute; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + + if (modifyListener !is null && !combo.isDisposed()) { + combo.removeModifyListener(modifyListener); + } + } + + private void notifyIfChanged(String oldValue, String newValue) { + if (!oldValue.equals(newValue)) { + fireValueChange(Diffs.createValueDiff(oldValue, newValue)); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSingleSelectionObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSingleSelectionObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bugs 198903, 198904 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ComboSingleSelectionObservableValue; + +import java.lang.all; + +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Combo; + +/** + * @since 1.0 + * + */ +public class ComboSingleSelectionObservableValue : + SingleSelectionObservableValue { + + private SelectionListener selectionListener; + + /** + * @param combo + */ + public this(Combo combo) { + super(combo); + } + + private Combo getCombo() { + return cast(Combo) getWidget(); + } + + protected void doAddSelectionListener(Runnable runnable) { + selectionListener = new class(runnable) SelectionListener { + Runnable runnable_; + this(Runnable r ){ runnable_=r; } + public void widgetDefaultSelected(SelectionEvent e) { + runnable_.run(); + } + + public void widgetSelected(SelectionEvent e) { + runnable_.run(); + } + }; + getCombo().addSelectionListener(selectionListener); + } + + protected int doGetSelectionIndex() { + return getCombo().getSelectionIndex(); + } + + protected void doSetSelectionIndex(int index) { + getCombo().select(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + if (selectionListener !is null && !getCombo().isDisposed()) { + getCombo().removeSelectionListener(selectionListener); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Matt Carter - bug 170668 + * Brad Reynolds - bug 170848 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ControlObservableValue; + +import java.lang.all; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.widgets.Control; + +/** + * @since 1.0 + * + */ +public class ControlObservableValue : AbstractSWTObservableValue { + + private final Control control; + + private final String attribute; + + private Object valueType; + + private static Map SUPPORTED_ATTRIBUTES; + static this() { + SUPPORTED_ATTRIBUTES = new HashMap(); + SUPPORTED_ATTRIBUTES.put(SWTProperties.ENABLED, Boolean.TYPE); + SUPPORTED_ATTRIBUTES.put(SWTProperties.VISIBLE, Boolean.TYPE); + SUPPORTED_ATTRIBUTES.put(SWTProperties.TOOLTIP_TEXT, String.classinfo); + SUPPORTED_ATTRIBUTES.put(SWTProperties.FOREGROUND, Color.classinfo); + SUPPORTED_ATTRIBUTES.put(SWTProperties.BACKGROUND, Color.classinfo); + SUPPORTED_ATTRIBUTES.put(SWTProperties.FONT, Font.classinfo); + } + + /** + * @param control + * @param attribute + */ + public this(Control control, String attribute) { + super(control); + this.control = control; + this.attribute = attribute; + if (SUPPORTED_ATTRIBUTES.keySet().contains(attribute)) { + this.valueType = SUPPORTED_ATTRIBUTES.get(attribute); + } else { + throw new IllegalArgumentException(); + } + } + + public void doSetValue(Object value) { + Object oldValue = doGetValue(); + if (attribute.equals(SWTProperties.ENABLED)) { + control.setEnabled((cast(Boolean) value).booleanValue()); + } else if (attribute.equals(SWTProperties.VISIBLE)) { + control.setVisible((cast(Boolean) value).booleanValue()); + } else if (attribute.equals(SWTProperties.TOOLTIP_TEXT)) { + control.setToolTipText(cast(String) value); + } else if (attribute.equals(SWTProperties.FOREGROUND)) { + control.setForeground(cast(Color) value); + } else if (attribute.equals(SWTProperties.BACKGROUND)) { + control.setBackground(cast(Color) value); + } else if (attribute.equals(SWTProperties.FONT)) { + control.setFont(cast(Font) value); + } + fireValueChange(Diffs.createValueDiff(oldValue, value)); + } + + public Object doGetValue() { + if (attribute.equals(SWTProperties.ENABLED)) { + return control.getEnabled() ? Boolean.TRUE : Boolean.FALSE; + } + if (attribute.equals(SWTProperties.VISIBLE)) { + return control.getVisible() ? Boolean.TRUE : Boolean.FALSE; + } + if (attribute.equals(SWTProperties.TOOLTIP_TEXT)) { + return control.getToolTipText(); + } + if (attribute.equals(SWTProperties.FOREGROUND)) { + return control.getForeground(); + } + if (attribute.equals(SWTProperties.BACKGROUND)) { + return control.getBackground(); + } + if (attribute.equals(SWTProperties.FONT)) { + return control.getFont(); + } + + return null; + } + + public Object getValueType() { + return valueType; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/DelayedObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/DelayedObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2007 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 180746) + * Boris Bokowski, IBM - initial API and implementation + * Matthew Hall - bug 212223 + * Will Horn - bug 215297 + * Matthew Hall - bug 208332 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.swt.DelayedObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.IValueChangeListener; +import org.eclipse.core.databinding.observable.value.IVetoableValue; +import org.eclipse.core.databinding.observable.value.ValueChangeEvent; +import org.eclipse.core.databinding.observable.value.ValueChangingEvent; +import org.eclipse.core.databinding.observable.value.ValueDiff; +import org.eclipse.jface.databinding.swt.ISWTObservableValue; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.jface.util.Util; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Widget; + +/** + * {@link IObservableValue} implementation that wraps any + * {@link ISWTObservableValue} and delays notification of value change events + * from the wrapped observable value until a certain time has passed since the + * last change event, or until a FocusOut event is received from the underlying + * widget (whichever happens earlier). This class helps to delay validation + * until the user stops typing. To notify about pending changes, a delayed + * observable value will fire a stale event when the wrapped observable value + * fires a change event, but this change is being delayed. + * + * Note that this class will not forward {@link ValueChangingEvent} events from + * a wrapped {@link IVetoableValue}. + * + * @since 1.2 + */ +public class DelayedObservableValue : AbstractSWTObservableValue { + class ValueUpdater : Runnable { + private final Object oldValue; + + bool cancel = false; + bool running = false; + + this(Object oldValue) { + this.oldValue = oldValue; + } + + void cancel() { + cancel = true; + } + + public void run() { + if (!cancel) + try { + running = true; + internalFireValueChange(oldValue); + } finally { + running = false; + } + } + } + + private IStaleListener staleListener = new class() IStaleListener { + public void handleStale(StaleEvent staleEvent) { + if (!updating) + fireStale(); + } + }; + + private IValueChangeListener valueChangeListener = new class() IValueChangeListener { + public void handleValueChange(ValueChangeEvent event) { + if (!updating) + makeDirty(); + } + }; + + private Listener focusOutListener = new class() Listener { + public void handleEvent(Event event) { + // Force update on focus out + if (dirty) + internalFireValueChange(cachedValue); + } + }; + + private final int delay; + private ISWTObservableValue observable; + private Control control; + + private bool dirty = true; + private Object cachedValue = null; + + private bool updating = false; + + private ValueUpdater updater = null; + + /** + * Constructs a new instance bound to the given + * ISWTObservableValue and configured to fire change events + * once there have been no value changes in the observable for + * delay milliseconds. + * + * @param delayMillis + * @param observable + * @throws IllegalArgumentException + * if updateEventType is an incorrect type. + */ + public this(int delayMillis, + ISWTObservableValue observable) { + super(observable.getRealm(), observable.getWidget()); + this.delay = delayMillis; + this.observable = observable; + + observable.addValueChangeListener(valueChangeListener); + observable.addStaleListener(staleListener); + Widget widget = observable.getWidget(); + if (null !is cast(Control)widget) { + control = cast(Control) widget; + control.addListener(SWT.FocusOut, focusOutListener); + } + + cachedValue = doGetValue(); + } + + protected Object doGetValue() { + if (dirty) { + cachedValue = observable.getValue(); + dirty = false; + + if (updater !is null && !updater.running) { + fireValueChange(Diffs.createValueDiff(updater.oldValue, + cachedValue)); + cancelScheduledUpdate(); + } + } + return cachedValue; + } + + protected void doSetValue(Object value) { + updating = true; + try { + // Principle of least surprise: setValue overrides any pending + // update from observable. + dirty = false; + cancelScheduledUpdate(); + + Object oldValue = cachedValue; + observable.setValue(value); + // Bug 215297 - target observable could veto or override value + // passed to setValue(). Make sure we cache whatever is set. + cachedValue = observable.getValue(); + + if (!Util.equals(oldValue, cachedValue)) + fireValueChange(Diffs.createValueDiff(oldValue, cachedValue)); + } finally { + updating = false; + } + } + + public bool isStale() { + ObservableTracker.getterCalled(this); + return (dirty && updater !is null) || observable.isStale(); + } + + /** + * Returns the type of the value from {@link #doGetValue()}, i.e. + * String.class + * + * @see org.eclipse.core.databinding.observable.value.IObservableValue#getValueType() + */ + public Object getValueType() { + return observable.getValueType(); + } + + public void dispose() { + cancelScheduledUpdate(); + if (observable !is null) { + observable.dispose(); + observable.removeValueChangeListener(valueChangeListener); + observable.removeStaleListener(staleListener); + observable = null; + } + if (control !is null) { + control.removeListener(SWT.FocusOut, focusOutListener); + control = null; + } + super.dispose(); + } + + private void makeDirty() { + if (!dirty) { + dirty = true; + fireStale(); + } + cancelScheduledUpdate(); // if any + scheduleUpdate(); + } + + private void cancelScheduledUpdate() { + if (updater !is null) { + updater.cancel(); + updater = null; + } + } + + private void scheduleUpdate() { + updater = new ValueUpdater(cachedValue); + observable.getWidget().getDisplay().timerExec(delay, updater); + } + + private void internalFireValueChange(Object oldValue) { + cancelScheduledUpdate(); + fireValueChange(new class(oldValue) ValueDiff { + Object oldValue_; + this(Object o){ oldValue_ = o; } + public Object getOldValue() { + return oldValue_; + } + + public Object getNewValue() { + return getValue(); + } + }); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.LabelObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.widgets.Label; + +/** + * @since 3.3 + * + */ +public class LabelObservableValue : AbstractSWTObservableValue { + + private final Label label; + + /** + * @param label + */ + public this(Label label) { + super(label); + this.label = label; + } + + /** + * @param realm + * @param label + */ + public this(Realm realm, Label label) { + super(realm, label); + this.label = label; + } + + public void doSetValue(Object value) { + String oldValue = label.getText(); + String newValue = value is null ? "" : value.toString(); //$NON-NLS-1$ + label.setText(newValue); + + if (!newValue.opEquals(oldValue)) { + fireValueChange(Diffs.createValueDiff(oldValue, newValue)); + } + } + + public Object doGetValue() { + return label.getText(); + } + + public Object getValueType() { + return String.classinfo; + } + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ListObservableList; + +import java.lang.all; + +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.swt.widgets.List; + +/** + * @since 3.2 + * + */ +public class ListObservableList : SWTObservableList { + + private final List list; + + /** + * @param list + */ + public this(List list) { + super(SWTObservables.getRealm(list.getDisplay())); + this.list = list; + } + + protected int getItemCount() { + return list.getItemCount(); + } + + protected void setItems(String[] newItems) { + list.setItems(newItems); + } + + protected String[] getItems() { + return list.getItems(); + } + + protected String getItem(int index) { + return list.getItem(index); + } + + protected void setItem(int index, String string) { + list.setItem(index, string); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ListObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.SWT; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.List; +import org.eclipse.swt.widgets.Listener; + +/** + * @since 3.2 + * + */ +public class ListObservableValue : AbstractSWTObservableValue { + + private final List list; + + private bool updating = false; + + private String currentValue; + + private Listener listener; + + /** + * @param list + */ + public this(List list) { + super(list); + this.list = list; + this.currentValue = cast(String) doGetValue(); + + if ((list.getStyle() & SWT.MULTI) > 0) + throw new IllegalArgumentException( + "SWT.SINGLE support only for a List selection"); //$NON-NLS-1$ + + listener = new class() Listener { + + public void handleEvent(Event event) { + if (!updating) { + Object oldValue = currentValue; + currentValue = cast(String) doGetValue(); + fireValueChange(Diffs.createValueDiff(oldValue, + currentValue)); + } + } + + }; + list.addListener(SWT.Selection, listener); + } + + public void doSetValue(Object value) { + String oldValue = null; + if (list.getSelection() !is null && list.getSelection().length > 0) + oldValue = list.getSelection()[0]; + try { + updating = true; + String items[] = list.getItems(); + int index = -1; + if (items !is null && value !is null) { + for (int i = 0; i < items.length; i++) { + if (value.equals(items[i])) { + index = i; + break; + } + } + list.select(index); // -1 will not "unselect" + } + currentValue = cast(String) value; + } finally { + updating = false; + } + fireValueChange(Diffs.createValueDiff(oldValue, value)); + } + + public Object doGetValue() { + int index = list.getSelectionIndex(); + if (index >= 0) + return list.getItem(index); + return null; + } + + public Object getValueType() { + return String.classinfo; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + if (listener !is null && !list.isDisposed()) { + list.removeListener(SWT.Selection, listener); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSingleSelectionObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSingleSelectionObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ListSingleSelectionObservableValue; + +import java.lang.all; + +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.List; + +/** + * @since 1.0 + * + */ +public class ListSingleSelectionObservableValue : + SingleSelectionObservableValue { + + private SelectionListener selectionListener; + + /** + * @param combo + */ + public this(List combo) { + super(combo); + } + + private List getList() { + return cast(List) getWidget(); + } + + protected void doAddSelectionListener(Runnable runnable) { + selectionListener = new class(runnable) SelectionListener { + Runnable runnable_; + this(Runnable r){ runnable_ = r; } + public void widgetDefaultSelected(SelectionEvent e) { + runnable_.run(); + } + + public void widgetSelected(SelectionEvent e) { + runnable_.run(); + } + }; + getList().addSelectionListener(selectionListener); + } + + protected int doGetSelectionIndex() { + return getList().getSelectionIndex(); + } + + protected void doSetSelectionIndex(int index) { + getList().setSelection(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + if (selectionListener !is null && !getList().isDisposed()) { + getList().removeSelectionListener(selectionListener); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,195 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matthew Hall - bug 208858 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.SWTObservableList; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.eclipse.core.databinding.BindingException; +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.ObservableTracker; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.AbstractObservableList; + +/** + * Abstract base class of CComboObservableList, ComboObservableList, and + * ListObservableList. + * + * @since 3.2 + * + */ +public abstract class SWTObservableList : AbstractObservableList { + + /** + * + */ + public this() { + super(); + } + + /** + * @param realm + */ + public this(Realm realm) { + super(realm); + } + + public void add(int index, Object element) { + int size = doGetSize(); + if (index < 0 || index > size) + index = size; + String[] newItems = new String[size + 1]; + System.arraycopy(getItems(), 0, newItems, 0, index); + newItems[index] = cast(String) element; + System.arraycopy(getItems(), index, newItems, index + 1, size - index); + setItems(newItems); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index, + true, element))); + } + + public int doGetSize() { + return getItemCount(); + } + + public Object get(int index) { + getterCalled(); + return getItem(index); + } + + public Object getElementType() { + return String.classinfo; + } + + /** + * @param index + * @return the item at the given index + */ + protected abstract String getItem(int index); + + /** + * @return the item count + */ + protected abstract int getItemCount(); + + /** + * @return the items + */ + protected abstract String[] getItems(); + + private void getterCalled() { + ObservableTracker.getterCalled(this); + } + + public Object remove(int index) { + getterCalled(); + int size = doGetSize(); + if (index < 0 || index > size - 1) + throw new BindingException( + "Request to remove an element out of the collection bounds"); //$NON-NLS-1$ + + String[] newItems = new String[size - 1]; + String oldElement = getItem(index); + if (newItems.length > 0) { + System.arraycopy(getItems(), 0, newItems, 0, index); + if (size - 1 > index) { + System.arraycopy(getItems(), index + 1, newItems, index, size + - index - 1); + } + } + setItems(newItems); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index, + false, oldElement))); + return oldElement; + } + + public Object set(int index, Object element) { + String oldElement = getItem(index); + setItem(index, cast(String) element); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(index, + false, oldElement), Diffs.createListDiffEntry(index, true, + element))); + return oldElement; + } + + public Object move(int oldIndex, int newIndex) { + checkRealm(); + if (oldIndex is newIndex) + return get(oldIndex); + int size = doGetSize(); + if (oldIndex < 0 || oldIndex >= size) + throw new IndexOutOfBoundsException( + "oldIndex: " + oldIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + if (newIndex < 0 || newIndex >= size) + throw new IndexOutOfBoundsException( + "newIndex: " + newIndex + ", size:" + size); //$NON-NLS-1$ //$NON-NLS-2$ + + String[] items = getItems(); + String[] newItems = new String[size]; + String element = items[oldIndex]; + if (newItems.length > 0) { + System.arraycopy(items, 0, newItems, 0, size); + if (oldIndex < newIndex) { + System.arraycopy(items, oldIndex + 1, newItems, oldIndex, + newIndex - oldIndex); + } else { + System.arraycopy(items, newIndex, newItems, newIndex + 1, + oldIndex - newIndex); + } + newItems[newIndex] = element; + } + setItems(newItems); + fireListChange(Diffs.createListDiff(Diffs.createListDiffEntry(oldIndex, + false, element), Diffs.createListDiffEntry(newIndex, true, + element))); + return element; + } + + public bool removeAll(Collection c) { + checkRealm(); + List oldItems = Arrays.asList(getItems()); + List newItems = new ArrayList(oldItems); + bool removedAll = newItems.removeAll(c); + if (removedAll) { + setItems(cast(String[]) newItems.toArray(new String[newItems.size()])); + fireListChange(Diffs.computeListDiff(oldItems, newItems)); + } + return removedAll; + } + + public bool retainAll(Collection c) { + checkRealm(); + List oldItems = Arrays.asList(getItems()); + List newItems = new ArrayList(oldItems); + bool retainedAll = newItems.retainAll(c); + if (retainedAll) { + setItems(cast(String[]) newItems.toArray(new String[newItems.size()])); + fireListChange(Diffs.computeListDiff(oldItems, newItems)); + } + return retainedAll; + } + + /** + * @param index + * @param string + */ + protected abstract void setItem(int index, String string); + + /** + * @param newItems + */ + protected abstract void setItems(String[] newItems); + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTProperties.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTProperties.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Matt Carter - bug 170668 + * Brad Reynolds - bug 170848 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.SWTProperties; + +import java.lang.all; + +/** + * Constants used to describe properties of SWT controls. + * + * @since 1.0 + * + */ +public interface SWTProperties { + + /** + * Applies to Control + */ + public static final String ENABLED = "enabled"; //$NON-NLS-1$ + /** + * Applies to Control + */ + public static final String VISIBLE = "visible"; //$NON-NLS-1$ + /** + * Applies to Control + */ + public static final String TOOLTIP_TEXT = "tooltip"; //$NON-NLS-1$ + /** + * Applies to + */ + public static final String ITEMS = "items"; //$NON-NLS-1$ + /** + * Applies to Spinner + */ + public static final String MAX = "max"; //$NON-NLS-1$ + /** + * Applies to Spinner + */ + public static final String MIN = "min"; //$NON-NLS-1$ + /** + * Applies to Spinner, Button + */ + public static final String SELECTION = "selection"; //$NON-NLS-1$ + /** + * Applies to Spinner, Button + */ + public static final String SELECTION_INDEX = "index"; //$NON-NLS-1$ + /** + * Applies to Text, Label, Combo + */ + public static final String TEXT = "text"; //$NON-NLS-1$ + + /** + * Applies to Label, CLabel. + */ + public static final String IMAGE = "image"; //$NON-NLS-1$ + /** + * Applies to Control + */ + public static final String FOREGROUND = "foreground"; //$NON-NLS-1$ + /** + * Applies to Control + */ + public static final String BACKGROUND = "background"; //$NON-NLS-1$ + /** + * Applies to Control + */ + public static final String FONT = "font"; //$NON-NLS-1$ + +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Peter Centgraf - bug 175763 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ScaleObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Scale; + +/** + * @since 1.0 + * + */ +public class ScaleObservableValue : AbstractSWTObservableValue { + + private final Scale scale; + + private final String attribute; + + private bool updating = false; + + private int currentSelection; + + private SelectionListener listener; + + /** + * @param scale + * @param attribute + */ + public this(Scale scale, String attribute) { + super(scale); + this.scale = scale; + this.attribute = attribute; + init(); + } + + /** + * @param realm + * @param scale + * @param attribute + */ + public this(Realm realm, Scale scale, String attribute) { + super(realm, scale); + this.scale = scale; + this.attribute = attribute; + init(); + } + + private void init() { + if (attribute.equals(SWTProperties.SELECTION)) { + currentSelection = scale.getSelection(); + scale.addSelectionListener(listener = new class() SelectionAdapter { + public void widgetSelected(SelectionEvent e) { + if (!updating) { + int newSelection = this.outer.scale + .getSelection(); + notifyIfChanged(currentSelection, newSelection); + currentSelection = newSelection; + } + } + }); + } else if (!attribute.equals(SWTProperties.MIN) + && !attribute.equals(SWTProperties.MAX)) { + throw new IllegalArgumentException( + "Attribute name not valid: " + attribute); //$NON-NLS-1$ + } + } + + public void doSetValue(Object value) { + int oldValue; + int newValue; + try { + updating = true; + newValue = (cast(Integer) value).intValue(); + if (attribute.equals(SWTProperties.SELECTION)) { + oldValue = scale.getSelection(); + scale.setSelection(newValue); + currentSelection = newValue; + } else if (attribute.equals(SWTProperties.MIN)) { + oldValue = scale.getMinimum(); + scale.setMinimum(newValue); + } else if (attribute.equals(SWTProperties.MAX)) { + oldValue = scale.getMaximum(); + scale.setMaximum(newValue); + } else { + Assert.isTrue(false, "invalid attribute name:" + attribute); //$NON-NLS-1$ + return; + } + + notifyIfChanged(oldValue, newValue); + } finally { + updating = false; + } + } + + public Object doGetValue() { + int value = 0; + if (attribute.opEquals(SWTProperties.SELECTION)) { + value = scale.getSelection(); + } else if (attribute.equals(SWTProperties.MIN)) { + value = scale.getMinimum(); + } else if (attribute.equals(SWTProperties.MAX)) { + value = scale.getMaximum(); + } + return new Integer(value); + } + + public Object getValueType() { + return Integer.TYPE; + } + + /** + * @return attribute being observed + */ + public String getAttribute() { + return attribute; + } + + /* (non-Javadoc) + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + + if (listener !is null && !scale.isDisposed()) { + scale.removeSelectionListener(listener); + } + listener = null; + } + + private void notifyIfChanged(int oldValue, int newValue) { + if (oldValue !is newValue) { + fireValueChange(Diffs.createValueDiff(new Integer(oldValue), + new Integer(newValue))); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ShellObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ShellObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2007 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 207844) + * IBM Corporation - initial API and implementation + * Brad Reynolds - initial API and implementation + * Matthew Hall - bug 212235 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.ShellObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.widgets.Shell; + +/** + * An {@link IObservableValue} that tracks the text of a Shell. + * + * @since 1.2 + */ +public class ShellObservableValue : AbstractSWTObservableValue { + + private final Shell shell; + + /** + * Constructs a ShellObservableValue which tracks the text of the given + * Shell. + * + * @param shell + * the shell to track + */ + public this(Shell shell) { + super(shell); + this.shell = shell; + } + + /** + * Constructs a ShellObservableValue belonging to the given realm, which + * tracks the text of the given shell. + * + * @param realm + * the realm of the constructed observable + * @param shell + * the shell to track + */ + public this(Realm realm, Shell shell) { + super(realm, shell); + this.shell = shell; + } + + protected void doSetValue(Object value) { + String oldValue = shell.getText(); + String newValue = value is null ? "" : value.toString(); //$NON-NLS-1$ + shell.setText(newValue); + + if (!newValue.opEquals(oldValue)) { + fireValueChange(Diffs.createValueDiff(oldValue, newValue)); + } + } + + protected Object doGetValue() { + return shell.getText(); + } + + public Object getValueType() { + return String.classinfo; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SingleSelectionObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SingleSelectionObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.SingleSelectionObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.widgets.Control; + +/** + * @since 1.0 + * + */ +abstract public class SingleSelectionObservableValue : + AbstractSWTObservableValue { + + private bool updating = false; + + private int currentSelection; + + /** + * @param control + * the control + */ + public this(Control control) { + super(control); + init(); + } + + /** + * @param realm + * @param control + */ + public this(Realm realm, Control control) { + super(realm, control); + init(); + } + + private void init() { + currentSelection = doGetSelectionIndex(); + doAddSelectionListener(new class() Runnable{ + public void run() { + if (!updating) { + int newSelection = doGetSelectionIndex(); + notifyIfChanged(currentSelection, newSelection); + currentSelection = newSelection; + } + } + }); + } + + /** + * @param runnable + */ + protected abstract void doAddSelectionListener(Runnable runnable); + + public void doSetValue(Object value) { + try { + updating = true; + int intValue = (cast(Integer) value).intValue(); + doSetSelectionIndex(intValue); + notifyIfChanged(currentSelection, intValue); + currentSelection = intValue; + } finally { + updating = false; + } + } + + /** + * @param intValue + * the selection index + */ + protected abstract void doSetSelectionIndex(int intValue); + + public Object doGetValue() { + return new Integer(doGetSelectionIndex()); + } + + /** + * @return the selection index + */ + protected abstract int doGetSelectionIndex(); + + public Object getValueType() { + return Integer.TYPE; + } + + private void notifyIfChanged(int oldValue, int newValue) { + if (oldValue !is newValue) { + fireValueChange(Diffs.createValueDiff(new Integer( + oldValue), new Integer(newValue))); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + * Matthew Hall - bug 118516 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.SpinnerObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.widgets.Spinner; + +/** + * @since 1.0 + * + */ +public class SpinnerObservableValue : AbstractSWTObservableValue { + + private final Spinner spinner; + + private final String attribute; + + private bool updating = false; + + private int currentSelection; + + private ModifyListener modifyListener; + + /** + * @param spinner + * @param attribute + */ + public this(Spinner spinner, String attribute) { + super(spinner); + this.spinner = spinner; + this.attribute = attribute; + init(); + } + + /** + * @param realm + * @param spinner + * @param attribute + */ + public this(Realm realm, Spinner spinner, String attribute) { + super(realm, spinner); + this.spinner = spinner; + this.attribute = attribute; + init(); + } + + private void init() { + if (attribute.equals(SWTProperties.SELECTION)) { + currentSelection = spinner.getSelection(); + modifyListener = new class() ModifyListener { + public void modifyText(ModifyEvent e) { + if (!updating) { + int newSelection = this.outer.spinner + .getSelection(); + notifyIfChanged(currentSelection, newSelection); + currentSelection = newSelection; + } + } + }; + spinner.addModifyListener(modifyListener); + } else if (!attribute.equals(SWTProperties.MIN) + && !attribute.equals(SWTProperties.MAX)) { + throw new IllegalArgumentException( + Format("Attribute name not valid: {}", attribute)); //$NON-NLS-1$ + } + } + + public void doSetValue(Object value) { + int oldValue; + int newValue; + try { + updating = true; + newValue = (cast(Integer) value).intValue(); + if (attribute.equals(SWTProperties.SELECTION)) { + oldValue = spinner.getSelection(); + spinner.setSelection(newValue); + currentSelection = newValue; + } else if (attribute.equals(SWTProperties.MIN)) { + oldValue = spinner.getMinimum(); + spinner.setMinimum(newValue); + } else if (attribute.equals(SWTProperties.MAX)) { + oldValue = spinner.getMaximum(); + spinner.setMaximum(newValue); + } else { + Assert.isTrue(false, "invalid attribute name:" + attribute); //$NON-NLS-1$ + return; + } + notifyIfChanged(oldValue, newValue); + } finally { + updating = false; + } + } + + public Object doGetValue() { + int value = 0; + if (attribute.equals(SWTProperties.SELECTION)) { + value = spinner.getSelection(); + } else if (attribute.equals(SWTProperties.MIN)) { + value = spinner.getMinimum(); + } else if (attribute.equals(SWTProperties.MAX)) { + value = spinner.getMaximum(); + } + return new Integer(value); + } + + public Object getValueType() { + return Integer.TYPE; + } + + /** + * @return attribute being observed + */ + public String getAttribute() { + return attribute; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + if (modifyListener !is null && !spinner.isDisposed()) { + spinner.removeModifyListener(modifyListener); + } + } + + private void notifyIfChanged(int oldValue, int newValue) { + if (oldValue !is newValue) { + fireValueChange(Diffs.createValueDiff(new Integer(oldValue), + new Integer(newValue))); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableSingleSelectionObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableSingleSelectionObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 164653 + * Ashley Cambrell - bug 198904 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.TableSingleSelectionObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Table; + +/** + * @since 1.0 + * + */ +public class TableSingleSelectionObservableValue : + SingleSelectionObservableValue { + + private SelectionListener selectionListener; + + /** + * @param table + */ + public this(Table table) { + super(table); + } + + /** + * @param realm + * @param table + */ + public this(Realm realm, Table table) { + super(realm, table); + } + + private Table getTable() { + return cast(Table) getWidget(); + } + + protected void doAddSelectionListener(Runnable runnable) { + selectionListener = new class(runnable) SelectionListener { + Runnable runnable_; + this(Runnable r){ runnable_ = r; } + public void widgetDefaultSelected(SelectionEvent e) { + runnable_.run(); + } + + public void widgetSelected(SelectionEvent e) { + runnable_.run(); + } + }; + getTable().addSelectionListener(selectionListener); + } + + protected int doGetSelectionIndex() { + return getTable().getSelectionIndex(); + } + + protected void doSetSelectionIndex(int index) { + getTable().setSelection(index); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + super.dispose(); + if (selectionListener !is null && !getTable().isDisposed()) { + getTable().removeSelectionListener(selectionListener); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextEditableObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextEditableObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +module org.eclipse.jface.internal.databinding.swt.TextEditableObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTObservableValue; +import org.eclipse.swt.widgets.Text; + +/** + * Observable value for the editable property of a Text. + * + * @since 1.1 + */ +public class TextEditableObservableValue : AbstractSWTObservableValue { + private Text text; + + /** + * @param text + */ + public this(Text text) { + super(text); + this.text = text; + } + + /** + * @param realm + * @param text + */ + public this(Realm realm, Text text) { + super(realm, text); + this.text = text; + } + + /* (non-Javadoc) + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#doGetValue() + */ + protected Object doGetValue() { + return (text.getEditable()) ? Boolean.TRUE : Boolean.FALSE; + } + + /* (non-Javadoc) + * @see org.eclipse.core.databinding.observable.value.IObservableValue#getValueType() + */ + public Object getValueType() { + return Boolean.TYPE; + } + + /* (non-Javadoc) + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#doSetValue(java.lang.Object) + */ + protected void doSetValue(Object value) { + if (value is null) { + throw new IllegalArgumentException("Parameter value was null."); //$NON-NLS-1$ + } + + Boolean oldValue = new Boolean(text.getEditable()); + Boolean newValue = cast(Boolean) value; + + text.setEditable(newValue.booleanValue()); + + if (!oldValue.equals(newValue)) { + fireValueChange(Diffs.createValueDiff(oldValue, newValue)); + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,200 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds (bug 135446) + * Brad Reynolds - bug 164653 + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.swt.TextObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.jface.internal.databinding.provisional.swt.AbstractSWTVetoableValue; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.VerifyEvent; +import org.eclipse.swt.events.VerifyListener; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Text; + +/** + * {@link IObservable} implementation that wraps a {@link Text} widget. The time + * at which listeners should be notified about changes to the text is specified + * on construction. + * + *
    + *
    Events:
    + *
    If the update event type (specified on construction) is + * SWT.Modify a value change event will be fired on every key + * stroke. If the update event type is SWT.FocusOut a value + * change event will be fired on focus out. When in either mode if the user is + * entering text and presses [Escape] the value will be reverted back to the + * last value set using doSetValue(). Regardless of the update event type a + * value changing event will fire on verify to enable vetoing of changes.
    + *
    + * + * @since 1.0 + */ +public class TextObservableValue : AbstractSWTVetoableValue { + + /** + * {@link Text} widget that this is being observed. + */ + private final Text text; + + /** + * Flag to track when the model is updating the widget. When + * true the handlers for the SWT events should not process + * the event as this would cause an infinite loop. + */ + private bool updating = false; + + /** + * SWT event that on firing this observable will fire change events to its + * listeners. + */ + private final int updateEventType; + + /** + * Valid types for the {@link #updateEventType}. + */ + private static const int[] validUpdateEventTypes = [ SWT.Modify, + SWT.FocusOut, SWT.None ]; + + /** + * Previous value of the Text. + */ + private String oldValue; + + private Listener updateListener = new class() Listener { + public void handleEvent(Event event) { + if (!updating) { + String newValue = text.getText(); + + if (!newValue.equals(oldValue)) { + fireValueChange(Diffs.createValueDiff(oldValue, newValue)); + oldValue = newValue; + } + } + } + }; + + private VerifyListener verifyListener; + + /** + * Constructs a new instance bound to the given text widget + * and configured to fire change events to its listeners at the time of the + * updateEventType. + * + * @param text + * @param updateEventType + * SWT event constant as to what SWT event to update the model in + * response to. Appropriate values are: SWT.Modify, + * SWT.FocusOut, SWT.None. + * @throws IllegalArgumentException + * if updateEventType is an incorrect type. + */ + public this(Text text, int updateEventType) { + this(SWTObservables.getRealm(text.getDisplay()), text, updateEventType); + } + + /** + * Constructs a new instance. + * + * @param realm can not be null + * @param text + * @param updateEventType + */ + public this(Realm realm, Text text, int updateEventType) { + super(realm, text); + + bool eventValid = false; + for (int i = 0; !eventValid && i < validUpdateEventTypes.length; i++) { + eventValid = (updateEventType is validUpdateEventTypes[i]); + } + if (!eventValid) { + throw new IllegalArgumentException( + "UpdateEventType [" + updateEventType + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ + } + this.text = text; + this.updateEventType = updateEventType; + if (updateEventType !is SWT.None) { + text.addListener(updateEventType, updateListener); + } + + oldValue = text.getText(); + + verifyListener = new class() VerifyListener { + public void verifyText(VerifyEvent e) { + if (!updating) { + String currentText = text + .getText(); + String newText = currentText.substring(0, e.start) + e.text + + currentText.substring(e.end); + if (!fireValueChanging(Diffs.createValueDiff(currentText, + newText))) { + e.doit = false; + } + } + } + }; + text.addVerifyListener(verifyListener); + } + + /** + * Sets the bound {@link Text Text's} text to the passed value. + * + * @param value + * new value, String expected + * @see org.eclipse.core.databinding.observable.value.AbstractVetoableValue#doSetApprovedValue(java.lang.Object) + * @throws ClassCastException + * if the value is anything other than a String + */ + protected void doSetApprovedValue(Object value) { + try { + updating = true; + text.setText(value is null ? "" : value.toString()); //$NON-NLS-1$ + oldValue = text.getText(); + } finally { + updating = false; + } + } + + /** + * Returns the current value of the {@link Text}. + * + * @see org.eclipse.core.databinding.observable.value.AbstractVetoableValue#doGetValue() + */ + public Object doGetValue() { + return oldValue = text.getText(); + } + + /** + * Returns the type of the value from {@link #doGetValue()}, i.e. + * String.class + * + * @see org.eclipse.core.databinding.observable.value.IObservableValue#getValueType() + */ + public Object getValueType() { + return String.classinfo; + } + + public void dispose() { + if (!text.isDisposed()) { + if (updateEventType !is SWT.None) { + text.removeListener(updateEventType, updateListener); + } + text.removeVerifyListener(verifyListener); + } + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 124684) + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.CheckableCheckedElementsObservableSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.set.AbstractObservableSet; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ICheckable; + +/** + * + * @since 1.2 + */ +public class CheckableCheckedElementsObservableSet : + AbstractObservableSet { + private ICheckable checkable; + private Set wrappedSet; + private Object elementType; + private ICheckStateListener listener; + + /** + * Constructs a new instance on the given realm and checkable. + * + * @param realm + * the observable's realm + * @param checkable + * the ICheckable to track + * @param elementType + * type of elements in the set + */ + public this(Realm realm, + ICheckable checkable, Object elementType) { + this(realm, checkable, elementType, new HashSet()); + } + + /** + * Constructs a new instance of the given realm, and checkable, + * + * @param realm + * the observable's realm + * @param checkable + * the ICheckable to track + * @param elementType + * type of elements in the set + * @param wrappedSet + * the set being wrapped + */ + public this(Realm realm, + ICheckable checkable, Object elementType, Set wrappedSet) { + super(realm); + Assert.isNotNull(checkable, "Checkable cannot be null"); //$NON-NLS-1$ + Assert.isNotNull(wrappedSet, "Wrapped set cannot be null"); //$NON-NLS-1$ + this.checkable = checkable; + this.wrappedSet = wrappedSet; + this.elementType = elementType; + + listener = new class(wrappedSet) ICheckStateListener { + Set wrappedSet_; + this(Set s){ wrappedSet_ = wrappedSet;} + public void checkStateChanged(CheckStateChangedEvent event) { + Object element = event.getElement(); + if (event.getChecked()) { + if (wrappedSet_.add(element)) + fireSetChange(Diffs.createSetDiff(Collections + .singleton(element), Collections.EMPTY_SET)); + } else { + if (wrappedSet_.remove(element)) + fireSetChange(Diffs.createSetDiff( + Collections.EMPTY_SET, Collections + .singleton(element))); + } + } + }; + checkable.addCheckStateListener(listener); + } + + protected Set getWrappedSet() { + return wrappedSet; + } + + Set createDiffSet() { + return new HashSet(); + } + + public Object getElementType() { + return elementType; + } + + public bool add(Object o) { + getterCalled(); + bool added = wrappedSet.add(o); + if (added) { + checkable.setChecked(o, true); + fireSetChange(Diffs.createSetDiff(Collections.singleton(o), + Collections.EMPTY_SET)); + } + return added; + } + + public bool remove(Object o) { + getterCalled(); + bool removed = wrappedSet.remove(o); + if (removed) { + checkable.setChecked(o, false); + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.singleton(o))); + } + return removed; + } + + public bool addAll(Collection c) { + getterCalled(); + Set additions = createDiffSet(); + for (Iterator iterator = c.iterator(); iterator.hasNext();) { + Object element = iterator.next(); + if (wrappedSet.add(element)) { + checkable.setChecked(element, true); + additions.add(element); + } + } + bool changed = !additions.isEmpty(); + if (changed) + fireSetChange(Diffs.createSetDiff(additions, Collections.EMPTY_SET)); + return changed; + } + + public bool removeAll(Collection c) { + getterCalled(); + Set removals = createDiffSet(); + for (Iterator iterator = c.iterator(); iterator.hasNext();) { + Object element = iterator.next(); + if (wrappedSet.remove(element)) { + checkable.setChecked(element, false); + removals.add(element); + } + } + bool changed = !removals.isEmpty(); + if (changed) + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals)); + return changed; + } + + public bool retainAll(Collection c) { + getterCalled(); + + // To ensure that elements are compared correctly, e.g. ViewerElementSet + Set toRetain = createDiffSet(); + toRetain.addAll(c); + + Set removals = createDiffSet(); + for (Iterator iterator = wrappedSet.iterator(); iterator.hasNext();) { + Object element = iterator.next(); + if (!toRetain.contains(element)) { + iterator.remove(); + checkable.setChecked(element, false); + removals.add(element); + } + } + bool changed = !removals.isEmpty(); + if (changed) + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals)); + return changed; + } + + public void clear() { + removeAll(wrappedSet); + } + + public Iterator iterator() { + getterCalled(); + final Iterator wrappedIterator = wrappedSet.iterator(); + return new class() Iterator { + private Object last = null; + + public bool hasNext() { + getterCalled(); + return wrappedIterator.hasNext(); + } + + public Object next() { + getterCalled(); + return last = wrappedIterator.next(); + } + + public void remove() { + getterCalled(); + wrappedIterator.remove(); + checkable.setChecked(last, false); + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.singleton(last))); + } + }; + } + + public synchronized void dispose() { + if (checkable !is null) { + checkable.removeCheckStateListener(listener); + checkable = null; + listener = null; + } + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxViewerCheckedElementsObservableSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxViewerCheckedElementsObservableSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 124684) + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.CheckboxViewerCheckedElementsObservableSet; + +import java.lang.all; + +import java.util.Arrays; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.databinding.viewers.IViewerObservableSet; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.CheckboxTreeViewer; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; + +/** + * An observable set that tracks the checked elements in a CheckboxTableViewer + * or CheckboxTreeViewer + * + * @since 1.2 + */ +public class CheckboxViewerCheckedElementsObservableSet : + CheckableCheckedElementsObservableSet , IViewerObservableSet { + private StructuredViewer viewer; + + /** + * Constructs a new instance on the given realm and checkable. + * + * @param realm + * the observable's realm + * @param viewer + * the CheckboxTableViewer viewer to track. + * @param elementType + * type of elements in the set + */ + public this(Realm realm, + CheckboxTableViewer viewer, Object elementType) { + super(realm, viewer, elementType, createElementSet(viewer)); + this.viewer = viewer; + } + + /** + * Constructs a new instance on the given realm and checkable. + * + * @param realm + * the observable's realm + * @param viewer + * the CheckboxTreeViewer viewer to track. + * @param elementType + * type of elements in the set + */ + public this(Realm realm, + CheckboxTreeViewer viewer, Object elementType) { + super(realm, viewer, elementType, createElementSet(viewer)); + this.viewer = viewer; + } + + Set createDiffSet() { + return ViewerElementSet.withComparer(viewer.getComparer()); + } + + private static Set createElementSet(CheckboxTableViewer viewer) { + Set set = ViewerElementSet.withComparer(viewer.getComparer()); + set.addAll(Arrays.asList(viewer.getCheckedElements())); + return set; + } + + private static Set createElementSet(CheckboxTreeViewer viewer) { + Set set = ViewerElementSet.withComparer(viewer.getComparer()); + set.addAll(Arrays.asList(viewer.getCheckedElements())); + return set; + } + + public Viewer getViewer() { + return viewer; + } + + public synchronized void dispose() { + viewer = null; + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/LeafNodesSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/LeafNodesSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,262 @@ +/******************************************************************************* + * Copyright (c) 2006, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.viewers.LeafNodesSet; + +import java.lang.all; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IStaleListener; +import org.eclipse.core.databinding.observable.StaleEvent; +import org.eclipse.core.databinding.observable.set.AbstractObservableSet; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.ISetChangeListener; +import org.eclipse.core.databinding.observable.set.SetChangeEvent; +import org.eclipse.core.databinding.observable.set.SetDiff; +import org.eclipse.core.internal.databinding.observable.tree.IUnorderedTreeProvider; +import org.eclipse.core.internal.databinding.observable.tree.TreePath; + +/** + * This set consists of all leaf nodes from the given tree (that is, all nodes + * for which ITreeProvider.createChildSet returns null). + */ +public class LeafNodesSet : AbstractObservableSet { + + private HashSet leafNodes = new HashSet(); + + private HashMap mapElementsOntoNodeInfo = new HashMap(); + + private IUnorderedTreeProvider tree; + + private Object input; + + private int staleCount = 0; + + private class NodeInfo : IStaleListener, ISetChangeListener { + // Number of times the element occurs in the tree + private int count; + + // Element + private TreePath treePath; + + // Children set (or null if this is a leaf node) + IObservableSet children; + + private bool wasStale = false; + + /** + * @param treePath + */ + public this(TreePath treePath) { + this.treePath = treePath; + children = tree.createChildSet(this.treePath); + if (children !is null) { + children.addStaleListener(this); + children.addSetChangeListener(this); + } + count = 1; + } + + public void handleSetChange(SetChangeEvent event) { + processDiff(treePath, event.diff); + } + + public void handleStale(StaleEvent event) { + if (wasStale !is children.isStale()) { + if (wasStale) { + staleCount--; + } else { + staleCount++; + } + wasStale = !wasStale; + } + setStale(staleCount > 0); + } + + /** + * + */ + public void dispose() { + if (children !is null) { + children.dispose(); + children = null; + if (wasStale) { + staleCount--; + } + } + } + } + + /** + * Creates a set that will contain the leaf nodes from the given tree + * + * @param tree + * tree whose leaf nodes will be computed + */ + public this(IUnorderedTreeProvider tree) { + this(null, tree); + } + + /** + * Creates a set that will contain the leaf nodes from the given tree, and + * sets the root of the tree to the given element. + * + * @param initialInput + * root of the tree + * @param tree + * tree whose leaf nodes will be computed + */ + public this(Object initialInput, IUnorderedTreeProvider tree) { + super(tree.getRealm()); + this.tree = tree; + if (initialInput !is null) { + setInput(initialInput); + } + } + + private void processDiff(TreePath treePath, SetDiff diff) { + Set removals = new HashSet(); + HashSet additions = new HashSet(); + + for (Iterator iter = diff.getRemovals().iterator(); iter.hasNext();) { + Object next = iter.next(); + + elementRemoved(treePath.createChildPath(next), removals); + } + + for (Iterator iter = diff.getAdditions().iterator(); iter.hasNext();) { + Object next = iter.next(); + + elementDiscovered(treePath.createChildPath(next), additions); + } + + HashSet newRemovals = new HashSet(); + newRemovals.addAll(removals); + newRemovals.removeAll(additions); + + HashSet newAdditions = new HashSet(); + newAdditions.addAll(additions); + newAdditions.removeAll(removals); + + leafNodes.addAll(newAdditions); + leafNodes.removeAll(newRemovals); + + if (!newAdditions.isEmpty() || !newRemovals.isEmpty()) { + setStale(staleCount > 0); + fireSetChange(Diffs.createSetDiff(newAdditions, newRemovals)); + } + } + + /** + * Sets the root of the tree to the given element. + * + * @param input + * new root of the tree + */ + public void setInput(Object input) { + Set removals = Collections.EMPTY_SET; + Set additions = Collections.EMPTY_SET; + if (this.input !is null) { + removals = Collections.singleton(this.input); + } else if (input !is null) { + additions = Collections.singleton(input); + } + this.input = input; + processDiff(TreePath.EMPTY, Diffs.createSetDiff(additions, removals)); + } + + /** + * Called when an element is removed from the tree. The given HashSet will + * be filled in with all removed leaf nodes. + * + * @param treePath + * @param removals + */ + private void elementRemoved(TreePath treePath, Set removals) { + NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath); + + if (newNode !is null) { + newNode = new NodeInfo(treePath); + newNode.count--; + if (newNode.count is 0) { + mapElementsOntoNodeInfo.remove(treePath); + if (newNode.children !is null) { + for (Iterator iter = newNode.children.iterator(); iter + .hasNext();) { + Object next = iter.next(); + + elementRemoved(treePath.createChildPath(next), removals); + } + newNode.children.dispose(); + } else { + removals.add(treePath); + } + } + } + } + + /** + * Called when a new element is discovered in the tree. The given HashSet + * will be filled in with all newly discovered leaf nodes. + * + * @param treePath + * @param additions + */ + private void elementDiscovered(TreePath treePath, HashSet additions) { + NodeInfo newNode = cast(NodeInfo) mapElementsOntoNodeInfo.get(treePath); + + if (newNode is null) { + newNode = new NodeInfo(treePath); + mapElementsOntoNodeInfo.put(treePath, newNode); + if (newNode.children !is null) { + for (Iterator iter = newNode.children.iterator(); iter + .hasNext();) { + Object next = iter.next(); + + elementDiscovered(treePath.createChildPath(next), additions); + } + } else { + additions.add(treePath); + } + } else { + // If this node was already known, increment the reference count. + newNode.count++; + } + } + + protected Set getWrappedSet() { + return leafNodes; + } + + public Object getElementType() { + return Object.classinfo; + } + + public void dispose() { + for (Iterator iter = mapElementsOntoNodeInfo.values().iterator(); iter + .hasNext();) { + NodeInfo next = cast(NodeInfo) iter.next(); + + if (next.children !is null) { + next.dispose(); + } + } + + mapElementsOntoNodeInfo.clear(); + leafNodes.clear(); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ListViewerUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ListViewerUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 215531) + * Matthew Hall - bug 226765 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ListViewerUpdater; + +import java.lang.all; + +import org.eclipse.jface.viewers.AbstractListViewer; + +/** + * NON-API - A {@link ViewerUpdater} that updates {@link AbstractListViewer} + * instances. + * + * @since 1.2 + */ +class ListViewerUpdater : ViewerUpdater { + private AbstractListViewer viewer; + + this(AbstractListViewer viewer) { + super(viewer); + this.viewer = viewer; + } + + public void insert(Object element, int position) { + viewer.insert(element, position); + } + + public void remove(Object element, int position) { + viewer.remove(element); + } + + public void add(Object[] elements) { + viewer.add(elements); + } + + public void remove(Object[] elements) { + viewer.remove(elements); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 215531) + * Matthew Hall - bug 226765 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ObservableCollectionContentProvider; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.IObservableCollection; +import org.eclipse.core.databinding.observable.Observables; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.WritableValue; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.jface.viewers.AbstractListViewer; +import org.eclipse.jface.viewers.AbstractTableViewer; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Display; + +/** + * NON-API - Abstract base class for content providers where the viewer input is + * expected to be an {@link IObservableCollection}. + * + * @since 1.2 + */ +public abstract class ObservableCollectionContentProvider : + IStructuredContentProvider { + private IObservableValue viewerObservable; + + /** + * Element comparer used by the viewer (may be null). + */ + protected IElementComparer comparer; + + /** + * Interface for sending updates to the viewer. + */ + protected ViewerUpdater viewerUpdater; + + /** + * Observable set of all elements known to the content provider. Subclasses + * must add new elements to this set before adding them to the + * viewer, and must remove old elements from this set after removing + * them from the viewer. + */ + protected IObservableSet knownElements; + + private IObservableSet unmodifiableKnownElements; + private IObservableCollection observableCollection; + + /** + * Constructs an ObservableCollectionContentProvider + */ + protected this() { + final Realm realm = SWTObservables.getRealm(Display.getDefault()); + viewerObservable = new WritableValue(realm); + viewerUpdater = null; + + // Known elements is a detail set of viewerObservable, so that when we + // get the viewer instance we can swap in a set that uses its + // IElementComparer, if any. + IObservableFactory knownElementsFactory = new class() IObservableFactory { + public IObservable createObservable(Object target) { + IElementComparer comparer = null; + if (null !is cast(StructuredViewer)target) + comparer = (cast(StructuredViewer) target).getComparer(); + return ObservableViewerElementSet.withComparer(realm, null, + comparer); + } + }; + knownElements = MasterDetailObservables.detailSet(viewerObservable, + knownElementsFactory, null); + unmodifiableKnownElements = Observables + .unmodifiableObservableSet(knownElements); + + observableCollection = null; + } + + public Object[] getElements(Object inputElement) { + if (observableCollection is null) + return new Object[0]; + return observableCollection.toArray(); + } + + public void dispose() { + if (observableCollection !is null) + removeCollectionChangeListener(observableCollection); + + if (viewerObservable !is null) { + viewerObservable.setValue(null); + viewerObservable.dispose(); + viewerObservable = null; + } + viewerUpdater = null; + knownElements = null; + unmodifiableKnownElements = null; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + setViewer(viewer); + setInput(newInput); + } + + private void setViewer(Viewer viewer) { + viewerUpdater = createViewerUpdater(viewer); + comparer = getElementComparer(viewer); + viewerObservable.setValue(viewer); // (clears knownElements) + } + + private static IElementComparer getElementComparer(Viewer viewer) { + if (null !is cast(StructuredViewer)viewer) + return (cast(StructuredViewer) viewer).getComparer(); + return null; + } + + ViewerUpdater createViewerUpdater(Viewer viewer) { + if (null !is cast(AbstractListViewer)viewer) + return new ListViewerUpdater(cast(AbstractListViewer) viewer); + if (null !is cast(AbstractTableViewer)viewer) + return new TableViewerUpdater(cast(AbstractTableViewer) viewer); + throw new IllegalArgumentException( + "This content provider only works with AbstractTableViewer or AbstractListViewer"); //$NON-NLS-1$ + } + + void setInput(Object input) { + if (observableCollection !is null) { + removeCollectionChangeListener(observableCollection); + observableCollection = null; + } + + if (input !is null) { + checkInput(input); + Assert.isTrue(null !is cast(IObservableCollection)input, + "Input must be an IObservableCollection"); //$NON-NLS-1$ + observableCollection = cast(IObservableCollection) input; + addCollectionChangeListener(observableCollection); + knownElements.addAll(observableCollection); + } + } + + /** + * Throws an exception if the input is not the correct type. + * + * @param input + * the input to check + */ + protected abstract void checkInput(Object input); + + /** + * Register for change event notification from the given collection. + * + * @param collection + * observable collection to listen to + */ + protected abstract void addCollectionChangeListener( + IObservableCollection collection); + + /** + * Deregisters from change events notification on the given collection. + * + * @param collection + * observable collection to stop listening to + */ + protected abstract void removeCollectionChangeListener( + IObservableCollection collection); + + /** + * Returns whether the viewer is disposed. Collection change listeners in + * subclasses should verify that the viewer is not disposed before sending + * any updates to the {@link ViewerUpdater viewer updater}. + * + * @return whether the viewer is disposed. + */ + protected final bool isViewerDisposed() { + Viewer viewer = cast(Viewer) viewerObservable.getValue(); + return viewer is null || viewer.getControl() is null + || viewer.getControl().isDisposed(); + } + + /** + * Returns the set of elements known to this content provider. Label + * providers may track this set if they need to be notified about additions + * before the viewer sees the added element, and notified about removals + * after the element was removed from the viewer. This is intended for use + * by label providers, as it will always return the items that need labels. + * + * @return unmodifiable observable set of items that will need labels + */ + public IObservableSet getKnownElements() { + return unmodifiableKnownElements; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,390 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 207858) + * Matthew Hall - bug 226765 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ObservableCollectionTreeContentProvider; + +import java.lang.all; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.databinding.observable.IObservable; +import org.eclipse.core.databinding.observable.IObservableCollection; +import org.eclipse.core.databinding.observable.IObservablesListener; +import org.eclipse.core.databinding.observable.Observables; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory; +import org.eclipse.core.databinding.observable.masterdetail.MasterDetailObservables; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.observable.value.WritableValue; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.jface.databinding.viewers.TreeStructureAdvisor; +import org.eclipse.jface.util.Util; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Display; + +/** + * NON-API - Abstract base class for {@link ITreeContentProvider}s which use an + * {@link IObservableFactory observable collection factory} to provide the + * elements of a tree. Each observable collection obtained from the factory is + * observed such that changes in the collection are reflected in the viewer. + * + * @since 1.2 + */ +public abstract class ObservableCollectionTreeContentProvider : + ITreeContentProvider { + private Realm realm; + + private IObservableValue viewerObservable; + + /** + * Interfaces for sending updates to the viewer. + */ + protected TreeViewerUpdater viewerUpdater; + + /** + * Element comparer used by the viewer (may be null). + */ + protected IElementComparer comparer; + + private IObservableSet knownElements; + private IObservableSet unmodifiableKnownElements; + + private IObservableFactory /* */collectionFactory; + + private Map /* */elementNodes; + + private TreeStructureAdvisor structureAdvisor; + + /** + * Constructs an ObservableCollectionTreeContentProvider using the given + * parent provider and collection factory. + * + * @param collectionFactory + * observable factory that produces an IObservableList of + * children for a given parent element. + * @param structureAdvisor + */ + protected this( + IObservableFactory collectionFactory, + TreeStructureAdvisor structureAdvisor) { + this.structureAdvisor = structureAdvisor; + realm = SWTObservables.getRealm(Display.getDefault()); + viewerObservable = new WritableValue(realm); + viewerUpdater = null; + + // Known elements is a detail set of viewerObservable, so that when we + // get the viewer instance we can swap in a set that uses its + // IElementComparer, if any. + IObservableFactory knownElementsFactory = new class() IObservableFactory { + public IObservable createObservable(Object target) { + return ObservableViewerElementSet.withComparer(realm, null, + getElementComparer(cast(Viewer) target)); + } + }; + knownElements = MasterDetailObservables.detailSet(viewerObservable, + knownElementsFactory, null); + unmodifiableKnownElements = Observables + .unmodifiableObservableSet(knownElements); + + Assert + .isNotNull(collectionFactory, + "Collection factory cannot be null"); //$NON-NLS-1$ + this.collectionFactory = collectionFactory; + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (elementNodes !is null && !elementNodes.isEmpty()) { + // Ensure we flush any observable collection listeners + TreeNode[] oldNodes = new TreeNode[elementNodes.size()]; + elementNodes.values().toArray(oldNodes); + for (int i = 0; i < oldNodes.length; i++) + oldNodes[i].dispose(); + elementNodes.clear(); + elementNodes = null; + } + + setViewer(viewer); + } + + private void setViewer(Viewer viewer) { + viewerUpdater = createViewerUpdater(viewer); + comparer = getElementComparer(viewer); + elementNodes = ViewerElementMap.withComparer(comparer); + viewerObservable.setValue(viewer); // (clears knownElements) + } + + private static IElementComparer getElementComparer(Viewer viewer) { + if (null !is cast(StructuredViewer)viewer) + return (cast(StructuredViewer) viewer).getComparer(); + return null; + } + + private static TreeViewerUpdater createViewerUpdater(Viewer viewer) { + if (null !is cast(AbstractTreeViewer)viewer) + return new TreeViewerUpdater(cast(AbstractTreeViewer) viewer); + throw new IllegalArgumentException( + "This content provider only works with AbstractTreeViewer"); //$NON-NLS-1$ + } + + public Object getParent(Object element) { + if (structureAdvisor !is null) { + Object parentFromAdvisor = structureAdvisor.getParent(element); + if (parentFromAdvisor !is null) { + return parentFromAdvisor; + } + } + TreeNode node = getExistingNode(element); + if (node !is null) + return node.getParent(); + return null; + } + + public Object[] getElements(Object input) { + return getChildren(input); + } + + public Object[] getChildren(Object element) { + Object[] children = getOrCreateNode(element).getChildren(); + for (int i = 0; i < children.length; i++) + getOrCreateNode(children[i]).addParent(element); + return children; + } + + public bool hasChildren(Object element) { + if (structureAdvisor !is null) { + Boolean hasChildren = structureAdvisor.hasChildren(element); + if (hasChildren !is null) { + return hasChildren.booleanValue(); + } + } + return getOrCreateNode(element).hasChildren(); + } + + protected TreeNode getOrCreateNode(Object element) { + TreeNode node = getExistingNode(element); + if (node is null) { + node = new TreeNode(element); + } + return node; + } + + protected TreeNode getExistingNode(Object element) { + TreeNode node = cast(TreeNode) elementNodes.get(element); + return node; + } + + protected bool isViewerDisposed() { + Viewer viewer = cast(Viewer) viewerObservable.getValue(); + return viewer is null || viewer.getControl() is null + || viewer.getControl().isDisposed(); + } + + public void dispose() { + if (elementNodes !is null) { + if (!elementNodes.isEmpty()) { + TreeNode[] nodes = new TreeNode[elementNodes.size()]; + elementNodes.values().toArray(nodes); + for (int i = 0; i < nodes.length; i++) { + nodes[i].dispose(); + } + elementNodes.clear(); + } + elementNodes = null; + } + if (viewerObservable !is null) { + viewerObservable.setValue(null); + viewerObservable.dispose(); + viewerObservable = null; + } + viewerUpdater = null; + comparer = null; + knownElements = null; + unmodifiableKnownElements = null; + collectionFactory = null; + } + + /** + * Returns the set of elements known to this content provider. Label + * providers may track this set if they need to be notified about additions + * before the viewer sees the added element, and notified about removals + * after the element was removed from the viewer. This is intended for use + * by label providers, as it will always return the items that need labels. + * + * @return unmodifiable observable set of items that will need labels + */ + public IObservableSet getKnownElements() { + return unmodifiableKnownElements; + } + + /** + * Returns a listener which, when a collection change event is received, + * updates the tree viewer through the {@link #viewerUpdater} field, and + * maintains the adds and removes parents from the appropriate tree nodes. + * + * @param parentElement + * the element that is the parent element of all elements in the + * observable collection. + * @return a listener which updates the viewer when change events occur. + */ + protected abstract IObservablesListener createCollectionChangeListener( + Object parentElement); + + /** + * Registers the change listener to receive change events for the specified + * observable collection. + * + * @param collection + * the collection to observe for changes + * @param listener + * the listener that will receive collection change events. + */ + protected abstract void addCollectionChangeListener( + IObservableCollection collection, IObservablesListener listener); + + /** + * Unregisters the change listener from receving change events for the + * specified observable collection. + * + * @param collection + * the collection to stop observing. + * @param listener + * the listener to remove + */ + protected abstract void removeCollectionChangeListener( + IObservableCollection collection, IObservablesListener listener); + + protected final class TreeNode { + private Object element; + + private Object parent; + private Set parentSet; + + private IObservableCollection children; + + private IObservablesListener listener; + + this(Object element) { + Assert.isNotNull(element, "element cannot be null"); //$NON-NLS-1$ + this.element = element; + knownElements.add(element); + elementNodes.put(element, this); + } + + Object getElement() { + return element; + } + + private bool equal(Object left, Object right) { + if (comparer is null) + return Util.equals(left, right); + return comparer.equals(left, right); + } + + public void addParent(Object newParent) { + if (parent is null) { + parent = newParent; + } else if (!equal(parent, newParent)) { + if (parentSet is null) { + parentSet = ViewerElementSet.withComparer(comparer); + parentSet.add(parent); + } + parentSet.add(newParent); + } + } + + public void removeParent(Object oldParent) { + if (parentSet !is null) + parentSet.remove(oldParent); + + if (equal(parent, oldParent)) { + if (parentSet is null || parentSet.isEmpty()) { + parent = null; + } else { + Iterator iterator = parentSet.iterator(); + parent = iterator.next(); + iterator.remove(); + } + } + + if (parentSet !is null && parentSet.isEmpty()) + parentSet = null; + + if (parent is null) { + dispose(); + } + } + + private Object getParent() { + return parent; + } + + private void initChildren() { + if (children is null) { + children = cast(IObservableCollection) collectionFactory + .createObservable(element); + if (children is null) { + listener = null; + children = Observables.emptyObservableSet(realm); + } else { + Assert + .isTrue(Util.equals(realm, children.getRealm()), + "Children observable collection must be on the Display realm"); //$NON-NLS-1$ + listener = createCollectionChangeListener(element); + addCollectionChangeListener(children, listener); + } + } + } + + bool hasChildren() { + initChildren(); + return !children.isEmpty(); + } + + Object[] getChildren() { + initChildren(); + return children.toArray(); + } + + private void dispose() { + if (element !is null) { + elementNodes.remove(element); + knownElements.remove(element); + } + if (children !is null) { + for (Iterator iterator = children.iterator(); iterator + .hasNext();) { + TreeNode child = getExistingNode(iterator.next()); + if (child !is null) + child.removeParent(element); + } + if (listener !is null) + removeCollectionChangeListener(children, listener); + children.dispose(); + children = null; + } + element = null; + parent = null; + if (parentSet !is null) { + parentSet.clear(); + parentSet = null; + } + } + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableViewerElementSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableViewerElementSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 215531) + * Matthew Hall - bug 230267 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ObservableViewerElementSet; + +import java.lang.all; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.set.AbstractObservableSet; +import org.eclipse.core.databinding.observable.set.IObservableSet; +import org.eclipse.core.databinding.observable.set.WritableSet; +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.StructuredViewer; + +/** + * An {@link IObservableSet} of elements in a {@link StructuredViewer}. + * Elements of the set are compared using an {@link IElementComparer} instead of + * {@link #equals(Object)}. + *

    + * This class is not a strict implementation the {@link IObservableSet} + * interface. It intentionally violates the {@link Set} contract, which requires + * the use of {@link #equals(Object)} when comparing elements. This class is + * designed for use with {@link StructuredViewer} which uses + * {@link IElementComparer} for element comparisons. + * + * + * @since 1.2 + */ +public class ObservableViewerElementSet : AbstractObservableSet { + private Set wrappedSet; + private Object elementType; + private IElementComparer comparer; + + /** + * Constructs an ObservableViewerElementSet on the given {@link Realm} which + * uses the given {@link IElementComparer} to compare elements. + * + * @param realm + * the realm of the constructed set. + * @param elementType + * the element type of the constructed set. + * @param comparer + * the {@link IElementComparer} used to compare elements. + */ + public this(Realm realm, Object elementType, + IElementComparer comparer) { + super(realm); + + Assert.isNotNull(comparer); + this.wrappedSet = new ViewerElementSet(comparer); + this.elementType = elementType; + this.comparer = comparer; + } + + protected Set getWrappedSet() { + return wrappedSet; + } + + public Object getElementType() { + return elementType; + } + + public Iterator iterator() { + getterCalled(); + final Iterator wrappedIterator = wrappedSet.iterator(); + return new class() Iterator { + Object last; + + public bool hasNext() { + getterCalled(); + return wrappedIterator.hasNext(); + } + + public Object next() { + getterCalled(); + return last = wrappedIterator.next(); + } + + public void remove() { + getterCalled(); + wrappedIterator.remove(); + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.singleton(last))); + } + }; + } + + public bool add(Object o) { + getterCalled(); + bool changed = wrappedSet.add(o); + if (changed) + fireSetChange(Diffs.createSetDiff(Collections.singleton(o), + Collections.EMPTY_SET)); + return changed; + } + + public bool addAll(Collection c) { + getterCalled(); + Set additions = new ViewerElementSet(comparer); + for (Iterator iterator = c.iterator(); iterator.hasNext();) { + Object element = iterator.next(); + if (wrappedSet.add(element)) + additions.add(element); + } + bool changed = !additions.isEmpty(); + if (changed) + fireSetChange(Diffs.createSetDiff(additions, Collections.EMPTY_SET)); + return changed; + } + + public bool remove(Object o) { + getterCalled(); + bool changed = wrappedSet.remove(o); + if (changed) + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, + Collections.singleton(o))); + return changed; + } + + public bool removeAll(Collection c) { + getterCalled(); + Set removals = new ViewerElementSet(comparer); + for (Iterator iterator = c.iterator(); iterator.hasNext();) { + Object element = iterator.next(); + if (wrappedSet.remove(element)) + removals.add(element); + } + bool changed = !removals.isEmpty(); + if (changed) + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals)); + return changed; + } + + public bool retainAll(Collection c) { + getterCalled(); + Set removals = new ViewerElementSet(comparer); + Object[] toRetain = c.toArray(); + outer: for (Iterator iterator = wrappedSet.iterator(); iterator + .hasNext();) { + Object element = iterator.next(); + // Cannot rely on c.contains(element) because we must compare + // elements using IElementComparer. + for (int i = 0; i < toRetain.length; i++) { + if (comparer.equals(element, toRetain[i])) + continue outer; + } + iterator.remove(); + removals.add(element); + } + bool changed = !removals.isEmpty(); + if (changed) + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals)); + return changed; + } + + public void clear() { + getterCalled(); + if (!wrappedSet.isEmpty()) { + Set removals = wrappedSet; + wrappedSet = new ViewerElementSet(comparer); + fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals)); + } + } + + /** + * Returns an {@link IObservableSet} for holding viewer elements, using the + * given {@link IElementComparer} for comparisons. + * + * @param realm + * the realm of the returned observable + * @param elementType + * the element type of the returned set + * @param comparer + * the element comparer to use in element comparisons (may be + * null). If null, the returned set will compare elements + * according to the standard contract for {@link Set} interface + * contract. + * @return a Set for holding viewer elements, using the given + * {@link IElementComparer} for comparisons. + */ + public static IObservableSet withComparer(Realm realm, Object elementType, + IElementComparer comparer) { + if (comparer is null) + return new WritableSet(realm, Collections.EMPTY_SET, elementType); + return new ObservableViewerElementSet(realm, elementType, comparer); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderMultipleSelectionObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderMultipleSelectionObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2007 Peter Centgraf and others. + * 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: + * Peter Centgraf - initial API and implementation, bug 124683 + * Boris Bokowski, IBM Corporation - initial API and implementation + *******************************************************************************/ +module org.eclipse.jface.internal.databinding.viewers.SelectionProviderMultipleSelectionObservableList; + +import java.lang.all; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.list.ListDiff; +import org.eclipse.core.databinding.observable.list.WritableList; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; + +/** + * Observes multiple-selection of an {@link ISelectionProvider}. + * + * @since 1.2 + */ +public class SelectionProviderMultipleSelectionObservableList : + WritableList { + + protected ISelectionProvider selectionProvider; + protected bool handlingSelection; + protected bool updating; + protected SelectionListener selectionListener = new SelectionListener(); + + class SelectionListener : ISelectionChangedListener { + public void selectionChanged(SelectionChangedEvent event) { + if (updating) { + return; + } + handlingSelection = true; + try { + updateWrappedList(new ArrayList(getSelectionList(event.getSelection()))); + } finally { + handlingSelection = false; + } + } + } + + /** + * Create a new observable list based on the current selection of the given + * selection provider. Assumes that the selection provider provides + * structured selections. + * + * @param realm + * @param selectionProvider + * @param elementType + */ + public this(Realm realm, + ISelectionProvider selectionProvider, Object elementType) { + super(realm, new ArrayList(getSelectionList(selectionProvider)), elementType); + this.selectionProvider = selectionProvider; + selectionProvider.addSelectionChangedListener(selectionListener); + } + + protected void fireListChange(ListDiff diff) { + if (handlingSelection) { + super.fireListChange(diff); + } else { + // this is a bit of a hack - we are changing the diff to match the order + // of elements returned by the selection provider after we've set the + // selection. + updating = true; + try { + List oldList = getSelectionList(selectionProvider); + selectionProvider + .setSelection(new StructuredSelection(wrappedList)); + wrappedList = new ArrayList(getSelectionList(selectionProvider)); + super.fireListChange(Diffs.computeListDiff(oldList, wrappedList)); + } finally { + updating = false; + } + } + } + + protected static List getSelectionList(ISelectionProvider selectionProvider) { + if (selectionProvider is null) { + throw new IllegalArgumentException(); + } + return getSelectionList(selectionProvider.getSelection()); + } + + protected static List getSelectionList(ISelection sel) { + if (null !is cast(IStructuredSelection)sel) { + return (cast(IStructuredSelection) sel).toList(); + } + return Collections.EMPTY_LIST; + } + + public synchronized void dispose() { + selectionProvider.removeSelectionChangedListener(selectionListener); + selectionProvider = null; + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderSingleSelectionObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderSingleSelectionObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 137877 + * Brad Reynolds - bug 164653 + * Brad Reynolds - bug 147515 + * Ashley Cambrell - bug 198906 + *******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.SelectionProviderSingleSelectionObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; +import org.eclipse.jface.util.Util; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; + +/** + * Observes single selection of an ISelectionProvider. + * + * @since 1.1 + */ +public class SelectionProviderSingleSelectionObservableValue : + AbstractObservableValue { + + private final ISelectionProvider selectionProvider; + + private bool updating = false; + + private Object currentSelection; + + private ISelectionChangedListener selectionChangedListener; + + /** + * Constructs a new instance associated with the provided + * selectionProvider. In order to initialize itself properly + * the constructor invokes {@link #doGetValue()}. This could be dangerous + * for subclasses, see {@link #doGetValue()} for an explanation. + * + * @param realm + * + * @param selectionProvider + * @see #doGetValue() + */ + public this(Realm realm, + ISelectionProvider selectionProvider) { + super(realm); + if (selectionProvider is null) { + throw new IllegalArgumentException( + "The 'selectionProvider' parameter is null."); //$NON-NLS-1$ + } + + this.selectionProvider = selectionProvider; + this.currentSelection = doGetValue(); + + selectionChangedListener = new class() ISelectionChangedListener { + public void selectionChanged(SelectionChangedEvent event) { + if (!updating) { + Object oldSelection = currentSelection; + currentSelection = doGetValue(); + fireValueChange(Diffs.createValueDiff(oldSelection, + currentSelection)); + } + } + }; + selectionProvider.addSelectionChangedListener(selectionChangedListener); + } + + /** + * Sets the selection to the provided value. Value change + * events are fired after selection is set in the selection provider. + * + * @param value + * object to set as selected, null if wanting to + * remove selection + */ + public void doSetValue(Object value) { + try { + updating = true; + + Object oldSelection = currentSelection; + selectionProvider + .setSelection(value is null ? StructuredSelection.EMPTY + : new StructuredSelection(value)); + currentSelection = doGetValue(); + if (!Util.equals(oldSelection, currentSelection)) { + fireValueChange(Diffs.createValueDiff(oldSelection, + currentSelection)); + } + } finally { + updating = false; + } + } + + /** + * Retrieves the current selection. + *

    + * If a subclass overrides this method it must not depend upon the subclass + * to have been fully initialized before this method is invoked. + * doGetValue() is invoked by the + * {@link #SelectionProviderSingleSelectionObservableValue(Realm, ISelectionProvider) constructor} + * which means the subclass's constructor will not have fully executed + * before this method is invoked. + *

    + * + * @return selection will be an instance of + * IStructuredSelection if a selection exists, + * null if no selection + * @see #SelectionProviderSingleSelectionObservableValue(Realm, + * ISelectionProvider) + */ + protected Object doGetValue() { + ISelection selection = selectionProvider.getSelection(); + if (null !is cast(IStructuredSelection)selection) { + IStructuredSelection sel = cast(IStructuredSelection) selection; + return sel.getFirstElement(); + } + + return null; + } + + public Object getValueType() { + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose() + */ + public synchronized void dispose() { + selectionProvider + .removeSelectionChangedListener(selectionChangedListener); + super.dispose(); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TableViewerUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TableViewerUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 215531) + * Matthew Hall - bug 226765 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.TableViewerUpdater; + +import java.lang.all; + +import org.eclipse.jface.viewers.AbstractTableViewer; + +/** + * NON-API - A {@link ViewerUpdater} that updates {@link AbstractTableViewer} + * instances. + * + * @since 1.2 + */ +class TableViewerUpdater : ViewerUpdater { + private AbstractTableViewer viewer; + + this(AbstractTableViewer viewer) { + super(viewer); + this.viewer = viewer; + } + + public void insert(Object element, int position) { + viewer.insert(element, position); + } + + public void remove(Object element, int position) { + viewer.remove(element); + } + + public void replace(Object oldElement, Object newElement, int position) { + if (viewer.getComparator() is null && viewer.getFilters().length is 0) + viewer.replace(newElement, position); + else { + super.replace(oldElement, newElement, position); + } + } + + public void add(Object[] elements) { + viewer.add(elements); + } + + public void remove(Object[] elements) { + viewer.remove(elements); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TreeViewerUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TreeViewerUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 207858) + * Matthew Hall - bug 226765 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.TreeViewerUpdater; + +import java.lang.all; + +import org.eclipse.jface.util.Util; +import org.eclipse.jface.viewers.AbstractTreeViewer; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.ITreeSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; + +/** + * NON-API - An interface for sending updates to an {@link AbstractTreeViewer}. + * + * @since 1.2 + */ +public class TreeViewerUpdater { + private final AbstractTreeViewer viewer; + private final TreeViewer treeViewer; + + /** + * Constructs an ITreeViewerUpdater for updating the given viewer. + * + * @param viewer + * the viewer that will be updated + */ + public this(AbstractTreeViewer viewer) { + this.viewer = viewer; + if (null !is cast(TreeViewer)viewer) + treeViewer = cast(TreeViewer) viewer; + else + treeViewer = null; + } + + /** + * Insert the element into the viewer as a child of the specified parent + * element, at the specified position. + * + * @param parent + * the parent of the element being inserted + * @param element + * the element to insert + * @param position + * the position where the element is inserted + */ + public void insert(Object parent, Object element, int position) { + viewer.insert(parent, element, position); + } + + /** + * Replaces the specified element whenever it appears as a child of the + * specified parent element, at the given position with the new element. + * + * @param parent + * the parent of the element being replaced + * @param oldElement + * the element being replaced + * @param newElement + * the element that replaces oldElement + * @param position + * the position of the element being replaced. + */ + public void replace(Object parent, Object oldElement, Object newElement, + int position) { + if (treeViewer !is null && viewer.getComparator() is null + && viewer.getFilters().length is 0) { + treeViewer.replace(parent, position, newElement); + } else { + remove(parent, oldElement, position); + insert(parent, newElement, position); + } + } + + /** + * Moves the specified element from the specified old position to the + * specified new position, whenever it appears as a child of the specified + * parent element. No action is taken if the viewer has a sorter or + * filter(s). + * + * @param parent + * the parent of the element being moved + * @param element + * the element being moved + * @param oldPosition + * the position of the element before it is moved + * @param newPosition + * the position of the element after it is moved + */ + public void move(Object parent, Object element, int oldPosition, + int newPosition) { + if (viewer.getComparator() is null && viewer.getFilters().length is 0) { + + ITreeSelection selection = cast(ITreeSelection) viewer.getSelection(); + + remove(parent, element, oldPosition); + insert(parent, element, newPosition); + + // Preserve selection + if (!selection.isEmpty()) { + // If the moved element is selected (or an ancestor of a + // selected element), restore the selection. + IElementComparer comparer = viewer.getComparer(); + TreePath[] paths = selection.getPaths(); + outer: for (int i = 0; i < paths.length; i++) { + TreePath path = paths[i]; + for (int j = 0; j < path.getSegmentCount(); j++) { + Object pathElement = path.getSegment(j); + if (comparer is null ? Util + .equals(element, pathElement) : comparer + .equals(element, pathElement)) { + viewer.setSelection(selection); + break outer; + } + } + } + } + } + } + + /** + * Removes the element from the from whenever it appears as a child of the + * specified parent element, at the specified position. + * + * @param parent + * the parent of the element being removed + * @param element + * the element to remove + * @param position + * the position where the element is located + */ + public void remove(Object parent, Object element, int position) { + if (treeViewer !is null && viewer.getComparator() is null + && viewer.getFilters().length is 0) { + // Only TreeViewer has a remove-by-index method. + treeViewer.remove(parent, position); + } else { + viewer.remove(parent, [ element ]); + } + } + + /** + * Add the elements into the viewer as children of the specified parent + * element. + * + * @param parent + * the parent of the element being inserted + * @param elements + * the elements to insert + */ + public void add(Object parent, Object[] elements) { + viewer.add(parent, elements); + } + + /** + * Remove the elements from the viewer wherever they appear as children of + * the specified parent element. + * + * @param parent + * the parent of the elements being removed + * @param elements + * the elements to remove + */ + public void remove(Object parent, Object[] elements) { + viewer.remove(parent, elements); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementMap.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementMap.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,434 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 215531) + * Matthew Hall - bug 228125 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ViewerElementMap; + +import java.lang.all; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.util.Util; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.StructuredViewer; + +/** + * A {@link Map} whose keys are elements in a {@link StructuredViewer}. The + * keys in the map are compared using an {@link IElementComparer} instead of + * {@link #equals(Object)}. + *

    + * This class is not a strict implementation the {@link Map} interface. + * It intentionally violates the {@link Map} contract, which requires the use of + * {@link #equals(Object)} when comparing keys. This class is designed for use + * with {@link StructuredViewer} which uses {@link IElementComparer} for element + * comparisons. + * + * @since 1.2 + */ +public class ViewerElementMap : Map { + private Map wrappedMap; + private IElementComparer comparer; + + /** + * Constructs a ViewerElementMap using the given {@link IElementComparer}. + * + * @param comparer + * the {@link IElementComparer} used for comparing keys. + */ + public this(IElementComparer comparer) { + Assert.isNotNull(comparer); + this.wrappedMap = new HashMap(); + this.comparer = comparer; + } + + /** + * Constructs a ViewerElementMap containing all the entries in the specified + * map. + * + * @param map + * the map whose entries are to be added to this map. + * @param comparer + * the {@link IElementComparer} used for comparing keys. + */ + public this(Map map, IElementComparer comparer) { + this(comparer); + Assert.isNotNull(map); + putAll(map); + } + + public void clear() { + wrappedMap.clear(); + } + + public bool containsKey(Object key) { + return wrappedMap.containsKey(new ViewerElementWrapper(key, comparer)); + } + + public bool containsValue(Object value) { + return wrappedMap.containsValue(value); + } + + public Set entrySet() { + final Set wrappedEntrySet = wrappedMap.entrySet(); + return new class() Set { + public bool add(Object o) { + throw new UnsupportedOperationException(); + } + + public bool addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + wrappedEntrySet.clear(); + } + + public bool contains(Object o) { + for (Iterator iterator = iterator(); iterator.hasNext();) + if (iterator.next().equals(o)) + return true; + return false; + } + + public bool containsAll(Collection c) { + for (Iterator iterator = c.iterator(); iterator.hasNext();) + if (!contains(iterator.next())) + return false; + return true; + } + + public bool isEmpty() { + return wrappedEntrySet.isEmpty(); + } + + public Iterator iterator() { + final Iterator wrappedIterator = wrappedEntrySet.iterator(); + return new class() Iterator { + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public Object next() { + Map.Entry wrappedEntry = cast(Map.Entry) wrappedIterator + .next(); + return new class() Map.Entry { + public Object getKey() { + return (cast(ViewerElementWrapper) wrappedEntry.getKey()) + .unwrap(); + } + + public Object getValue() { + return wrappedEntry.getValue(); + } + + public Object setValue(Object value) { + return wrappedEntry.setValue(value); + } + + public bool equals(Object obj) { + if (obj is this) + return true; + if (obj is null || !(null !is cast(Map.Entry)obj)) + return false; + Map.Entry that = cast(Map.Entry) obj; + return comparer.equals(this.getKey(), that + .getKey()) + && Util.equals(this.getValue(), that + .getValue()); + } + + public int hashCode() { + return wrappedEntry.hashCode(); + } + }; + } + + public void remove() { + wrappedIterator.remove(); + } + }; + } + + public bool remove(Object o) { + Map.Entry unwrappedEntry = cast(Map.Entry) o; + final ViewerElementWrapper wrappedKey = new ViewerElementWrapper( + unwrappedEntry.getKey(), comparer); + Map.Entry wrappedEntry = new class() Map.Entry { + public Object getKey() { + return wrappedKey; + } + + public Object getValue() { + return unwrappedEntry.getValue(); + } + + public Object setValue(Object value) { + throw new UnsupportedOperationException(); + } + + public bool equals(Object obj) { + if (obj is this) + return true; + if (obj is null || !(null !is cast(Map.Entry)obj)) + return false; + Map.Entry that = cast(Map.Entry) obj; + return Util.equals(wrappedKey, that.getKey()) + && Util + .equals(this.getValue(), that + .getValue()); + } + + public int hashCode() { + return wrappedKey.hashCode() + ^ (getValue() is null ? 0 : getValue() + .hashCode()); + } + }; + return wrappedEntrySet.remove(wrappedEntry); + } + + public bool removeAll(Collection c) { + bool changed = false; + for (Iterator iterator = c.iterator(); iterator.hasNext();) + changed |= remove(iterator.next()); + return changed; + } + + public bool retainAll(Collection c) { + bool changed = false; + Object[] toRetain = c.toArray(); + outer: for (Iterator iterator = iterator(); iterator.hasNext();) { + Object entry = iterator.next(); + for (int i = 0; i < toRetain.length; i++) + if (entry.equals(toRetain[i])) + continue outer; + iterator.remove(); + changed = true; + } + return changed; + } + + public int size() { + return wrappedEntrySet.size(); + } + + public Object[] toArray() { + return toArray(new Object[size()]); + } + + public Object[] toArray(Object[] a) { + int size = size(); + if (a.length < size) { + a = cast(Object[]) Array.newInstance(a.getClass() + .getComponentType(), size); + } + int i = 0; + for (Iterator iterator = iterator(); iterator.hasNext();) { + a[i] = iterator.next(); + i++; + } + return a; + } + + public bool equals(Object obj) { + if (obj is this) + return true; + if (obj is null || !(null !is cast(Set)obj)) + return false; + Set that = cast(Set) obj; + return this.size() is that.size() && containsAll(that); + } + + public int hashCode() { + return wrappedEntrySet.hashCode(); + } + }; + } + + public Object get(Object key) { + return wrappedMap.get(new ViewerElementWrapper(key, comparer)); + } + + public bool isEmpty() { + return wrappedMap.isEmpty(); + } + + public Set keySet() { + final Set wrappedKeySet = wrappedMap.keySet(); + return new class() Set { + public bool add(Object o) { + throw new UnsupportedOperationException(); + } + + public bool addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + wrappedKeySet.clear(); + } + + public bool contains(Object o) { + return wrappedKeySet.contains(new ViewerElementWrapper(o, comparer)); + } + + public bool containsAll(Collection c) { + for (Iterator iterator = c.iterator(); iterator.hasNext();) + if (!wrappedKeySet.contains(new ViewerElementWrapper(iterator.next(), comparer))) + return false; + return true; + } + + public bool isEmpty() { + return wrappedKeySet.isEmpty(); + } + + public Iterator iterator() { + final Iterator wrappedIterator = wrappedKeySet.iterator(); + return new class() Iterator { + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public Object next() { + return (cast(ViewerElementWrapper) wrappedIterator.next()).unwrap(); + } + + public void remove() { + wrappedIterator.remove(); + } + }; + } + + public bool remove(Object o) { + return wrappedKeySet.remove(new ViewerElementWrapper(o, comparer)); + } + + public bool removeAll(Collection c) { + bool changed = false; + for (Iterator iterator = c.iterator(); iterator.hasNext();) + changed |= wrappedKeySet + .remove(new ViewerElementWrapper(iterator.next(), comparer)); + return changed; + } + + public bool retainAll(Collection c) { + bool changed = false; + Object[] toRetain = c.toArray(); + outer: for (Iterator iterator = iterator(); iterator.hasNext();) { + Object element = iterator.next(); + for (int i = 0; i < toRetain.length; i++) + if (comparer.equals(element, toRetain[i])) + continue outer; + // element not contained in collection, remove. + remove(element); + changed = true; + } + return changed; + } + + public int size() { + return wrappedKeySet.size(); + } + + public Object[] toArray() { + return toArray(new Object[wrappedKeySet.size()]); + } + + public Object[] toArray(Object[] a) { + int size = wrappedKeySet.size(); + ViewerElementWrapper[] wrappedArray = cast(ViewerElementWrapper[]) wrappedKeySet + .toArray(new ViewerElementWrapper[size]); + Object[] result = a; + if (a.length < size) { + result = cast(Object[]) Array.newInstance(a.getClass() + .getComponentType(), size); + } + for (int i = 0; i < size; i++) + result[i] = wrappedArray[i].unwrap(); + return result; + } + + public bool equals(Object obj) { + if (obj is this) + return true; + if (obj is null || !(null !is cast(Set)obj)) + return false; + Set that = cast(Set) obj; + return this.size() is that.size() && containsAll(that); + } + + public int hashCode() { + return wrappedKeySet.hashCode(); + } + }; + } + + public Object put(Object key, Object value) { + return wrappedMap.put(new ViewerElementWrapper(key, comparer), value); + } + + public void putAll(Map other) { + for (Iterator iterator = other.entrySet().iterator(); iterator + .hasNext();) { + Map.Entry entry = cast(Map.Entry) iterator.next(); + wrappedMap.put(new ViewerElementWrapper(entry.getKey(), comparer), entry.getValue()); + } + } + + public Object remove(Object key) { + return wrappedMap.remove(new ViewerElementWrapper(key, comparer)); + } + + public int size() { + return wrappedMap.size(); + } + + public Collection values() { + return wrappedMap.values(); + } + + public bool equals(Object obj) { + if (obj is this) + return true; + if (obj is null || !(null !is cast(Map)obj)) + return false; + Map that = cast(Map) obj; + return this.entrySet().equals(that.entrySet()); + } + + public int hashCode() { + return wrappedMap.hashCode(); + } + + /** + * Returns a Map for mapping viewer elements as keys to values, using the + * given {@link IElementComparer} for key comparisons. + * + * @param comparer + * the element comparer to use in key comparisons. If null, the + * returned map will compare keys according to the standard + * contract for {@link Map} interface contract. + * @return a Map for mapping viewer elements as keys to values, using the + * given {@link IElementComparer} for key comparisons. + */ + public static Map withComparer(IElementComparer comparer) { + if (comparer is null) + return new HashMap(); + return new ViewerElementMap(comparer); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementSet.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementSet.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 215531) + * Matthew Hall - bug 124684 + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ViewerElementSet; + +import java.lang.all; + +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.StructuredViewer; + +/** + * A {@link Set} of elements in a {@link StructuredViewer}. Elements of the set + * are compared using an {@link IElementComparer} instead of + * {@link #equals(Object)}. + *

    + * This class is not a strict implementation the {@link Set} interface. + * It intentionally violates the {@link Set} contract, which requires the use of + * {@link #equals(Object)} when comparing elements. This class is designed for + * use with {@link StructuredViewer} which uses {@link IElementComparer} for + * element comparisons. + * + * @since 1.2 + */ +public class ViewerElementSet : Set { + private final Set wrappedSet; + private final IElementComparer comparer; + + /** + * Constructs a ViewerElementSet using the given {@link IElementComparer}. + * + * @param comparer + * the {@link IElementComparer} used for comparing elements. + */ + public this(IElementComparer comparer) { + Assert.isNotNull(comparer); + this.wrappedSet = new HashSet(); + this.comparer = comparer; + } + + /** + * Constructs a ViewerElementSet containing all the elements in the + * specified collection. + * + * @param collection + * the collection whose elements are to be added to this set. + * @param comparer + * the {@link IElementComparer} used for comparing elements. + */ + public this(Collection collection, IElementComparer comparer) { + this(comparer); + addAll(collection); + } + + public bool add(Object o) { + return wrappedSet.add(new ViewerElementWrapper(o, comparer)); + } + + public bool addAll(Collection c) { + bool changed = false; + for (Iterator iterator = c.iterator(); iterator.hasNext();) + changed |= wrappedSet.add(new ViewerElementWrapper(iterator.next(), + comparer)); + return changed; + } + + public void clear() { + wrappedSet.clear(); + } + + public bool contains(Object o) { + return wrappedSet.contains(new ViewerElementWrapper(o, comparer)); + } + + public bool containsAll(Collection c) { + for (Iterator iterator = c.iterator(); iterator.hasNext();) + if (!wrappedSet.contains(new ViewerElementWrapper(iterator.next(), + comparer))) + return false; + return true; + } + + public bool isEmpty() { + return wrappedSet.isEmpty(); + } + + public Iterator iterator() { + final Iterator wrappedIterator = wrappedSet.iterator(); + return new class() Iterator { + public bool hasNext() { + return wrappedIterator.hasNext(); + } + + public Object next() { + return (cast(ViewerElementWrapper) wrappedIterator.next()).unwrap(); + } + + public void remove() { + wrappedIterator.remove(); + } + }; + } + + public bool remove(Object o) { + return wrappedSet.remove(new ViewerElementWrapper(o, comparer)); + } + + public bool removeAll(Collection c) { + bool changed = false; + for (Iterator iterator = c.iterator(); iterator.hasNext();) + changed |= remove(iterator.next()); + return changed; + } + + public bool retainAll(Collection c) { + // Have to do this the slow way to ensure correct comparisons. i.e. + // cannot delegate to c.contains(it) since we can't be sure will + // compare elements the way we want. + bool changed = false; + Object[] retainAll = c.toArray(); + outer: for (Iterator iterator = iterator(); iterator.hasNext();) { + Object element = iterator.next(); + for (int i = 0; i < retainAll.length; i++) { + if (comparer.equals(element, retainAll[i])) { + continue outer; + } + } + iterator.remove(); + changed = true; + } + return changed; + } + + public int size() { + return wrappedSet.size(); + } + + public Object[] toArray() { + return toArray(new Object[wrappedSet.size()]); + } + + public Object[] toArray(Object[] a) { + int size = wrappedSet.size(); + ViewerElementWrapper[] wrappedArray = cast(ViewerElementWrapper[]) wrappedSet + .toArray(new ViewerElementWrapper[size]); + Object[] result = a; + if (a.length < size) { + result = cast(Object[]) Array.newInstance(a.getClass() + .getComponentType(), size); + } + for (int i = 0; i < size; i++) + result[i] = wrappedArray[i].unwrap(); + return result; + } + + public bool equals(Object obj) { + if (obj is this) + return true; + if (!(null !is cast(Set)obj)) + return false; + Set that = cast(Set) obj; + return size() is that.size() && containsAll(that); + } + + public int hashCode() { + int hash = 0; + for (Iterator iterator = iterator(); iterator.hasNext();) { + Object element = iterator.next(); + hash += element is null ? 0 : element.hashCode(); + } + return hash; + } + + /** + * Returns a Set for holding viewer elements, using the given + * {@link IElementComparer} for comparisons. + * + * @param comparer + * the element comparer to use in element comparisons. If null, + * the returned set will compare elements according to the + * standard contract for {@link Set} interface contract. + * @return a Set for holding viewer elements, using the given + * {@link IElementComparer} for comparisons. + */ + public static Set withComparer(IElementComparer comparer) { + if (comparer is null) + return new HashSet(); + return new ViewerElementSet(comparer); + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementWrapper.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementWrapper.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 215531) + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ViewerElementWrapper; + +import java.lang.all; + +import org.eclipse.jface.viewers.IElementComparer; + +/** + * A wrapper class for viewer elements, which uses an {@link IElementComparer} + * for computing {@link Object#equals(Object) equality} and + * {@link Object#hashCode() hashes}. + * + * @since 1.2 + */ +public class ViewerElementWrapper { + private final Object element; + private final IElementComparer comparer; + + /** + * Constructs a ViewerElementWrapper wrapping the given element + * + * @param element + * the element being wrapped + * @param comparer + * the comparer to use for computing equality and hash codes. + */ + public this(Object element, IElementComparer comparer) { + if (comparer is null) + throw new NullPointerException(); + this.element = element; + this.comparer = comparer; + } + + public bool equals(Object obj) { + if (!(null !is cast(ViewerElementWrapper)obj)) { + return false; + } + return comparer.equals(element, (cast(ViewerElementWrapper) obj).element); + } + + public int hashCode() { + return comparer.hashCode(element); + } + + Object unwrap() { + return element; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerInputObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerInputObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2007 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 206839) + *******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ViewerInputObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.core.databinding.observable.value.AbstractObservableValue; +import org.eclipse.jface.util.Util; +import org.eclipse.jface.viewers.Viewer; + +/** + * Observes the input of a Viewer. + *

    + * This observer is blind to changes in the viewer's input unless its + * {@link #setValue(Object)} method is called directly. + * + * @since 1.2 + */ +public class ViewerInputObservableValue : AbstractObservableValue { + + private final Viewer viewer; + + /** + * Constructs a new instance associated with the provided viewer. + * + * @param realm + * @param viewer + */ + public this( Realm realm, Viewer viewer ) { + super( realm ); + if ( viewer is null ) { + throw new IllegalArgumentException( "The 'viewer' parameter is null." ); //$NON-NLS-1$ + } + + this.viewer = viewer; + } + + /** + * Sets the input to the provided value. Value change events are + * fired after input is set in the viewer. + * + * @param value object to set as input + */ + protected void doSetValue( Object value ) { + Object oldValue = doGetValue(); + viewer.setInput( value ); + if ( !Util.equals( oldValue, value ) ) { + fireValueChange( Diffs.createValueDiff( oldValue, value ) ); + } + } + + /** + * Retrieves the current input. + * + * @return the current input + */ + protected Object doGetValue() { + return viewer.getInput(); + } + + public Object getValueType() { + return null; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerMultipleSelectionObservableList.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerMultipleSelectionObservableList.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 137877 + * Brad Reynolds - bug 164653 + * Brad Reynolds - bug 147515 + * Ashley Cambrell - bug 198906 + *******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ViewerMultipleSelectionObservableList; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.databinding.viewers.IViewerObservableList; +import org.eclipse.jface.viewers.Viewer; + +/** + * Observes single selection of a Viewer. + * + * @since 1.2 + */ +public class ViewerMultipleSelectionObservableList : + SelectionProviderMultipleSelectionObservableList , + IViewerObservableList { + + private Viewer viewer; + + /** + * @param realm + * @param viewer + * @param elementType + */ + public this(Realm realm, Viewer viewer, + Object elementType) { + super(realm, viewer, elementType); + this.viewer = viewer; + } + + public Viewer getViewer() { + return viewer; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerSingleSelectionObservableValue.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerSingleSelectionObservableValue.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2005, 2008 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + * Brad Reynolds - bug 137877 + * Brad Reynolds - bug 164653 + * Brad Reynolds - bug 147515 + * Ashley Cambrell - bug 198906 + *******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ViewerSingleSelectionObservableValue; + +import java.lang.all; + +import org.eclipse.core.databinding.observable.Realm; +import org.eclipse.jface.databinding.viewers.IViewerObservableValue; +import org.eclipse.jface.viewers.Viewer; + +/** + * Observes single selection of a Viewer. + * + * @since 1.2 + */ +public class ViewerSingleSelectionObservableValue : + SelectionProviderSingleSelectionObservableValue , + IViewerObservableValue { + + private Viewer viewer; + + /** + * @param realm + * @param viewer + */ + public this(Realm realm, Viewer viewer) { + super(realm, viewer); + this.viewer = viewer; + } + + public Viewer getViewer() { + return viewer; + } +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerUpdater.d --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerUpdater.d Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2008 Matthew Hall and others. + * 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: + * Matthew Hall - initial API and implementation (bug 226765) + ******************************************************************************/ + +module org.eclipse.jface.internal.databinding.viewers.ViewerUpdater; + +import java.lang.all; + +import org.eclipse.jface.util.Util; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredViewer; + +/** + * NON-API - An interface for updating a viewer's elements. + * + * @since 1.2 + */ +public abstract class ViewerUpdater { + private final StructuredViewer viewer; + + /** + * Constructs a ViewerUpdater for updating the specified viewer. + * + * @param viewer + * the viewer which will be updated through this instance. + */ + protected this(StructuredViewer viewer) { + this.viewer = viewer; + } + + /** + * Insert the element into the viewer at the specified position. + * + * @param element + * the element to add + * @param position + * the position of the element + */ + public abstract void insert(Object element, int position); + + /** + * Remove the element from the viewer + * + * @param element + * the element to remove + * @param position + * the position of the element + */ + public abstract void remove(Object element, int position); + + /** + * Replace the specified element at the given position with the new element. + * + * @param oldElement + * the element being replaced + * @param newElement + * the element that replaces oldElement + * @param position + * the position of the element being replaced. + */ + public void replace(Object oldElement, Object newElement, int position) { + remove(oldElement, position); + insert(newElement, position); + } + + /** + * Moves the specified element from the specified old position to the + * specified new position. No action is taken if the viewer has a sorter or + * filter(s). + * + * @param element + * the element being moved + * @param oldPosition + * the position of the element before it is moved + * @param newPosition + * the position of the element after it is moved + */ + public void move(Object element, int oldPosition, int newPosition) { + if (viewer.getComparator() is null && viewer.getFilters().length is 0) { + IStructuredSelection selection = cast(IStructuredSelection) viewer + .getSelection(); + + remove(element, oldPosition); + insert(element, newPosition); + + // Preserve selection + if (!selection.isEmpty()) { + IElementComparer comparer = viewer.getComparer(); + Object[] selectedElements = selection.toArray(); + for (int i = 0; i < selectedElements.length; i++) { + if (comparer is null ? Util.equals(element, + selectedElements[i]) : comparer.equals(element, + selectedElements[i])) { + viewer.setSelection(selection); + break; + } + } + } + } + } + + /** + * Adds the elements to the viewer. + * + * @param elements + * the elements to add + */ + public abstract void add(Object[] elements); + + /** + * Removes the elements from the viewer + * + * @param elements + * the elements to remove + */ + public abstract void remove(Object[] elements); +} diff -r f05e6e8b2f2d -r 0a55d2d5a946 org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.d --- a/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.d Sun Apr 12 12:27:13 2009 +0200 +++ b/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.d Tue Apr 14 11:35:29 2009 +0200 @@ -28,6 +28,24 @@ import java.lang.all; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.IJobChangeListener; +import tango.io.Stdout; + +class MyJob : Job { + this(char[] name) { + super(name); + } + public IStatus run(IProgressMonitor monitor) { + Stdout.formatln("doing job"); + return Status.OK_STATUS; + } +} + + void main (String [] args) { Display display = new Display (); Cursor cursor = new Cursor (display, SWT.CURSOR_HAND); @@ -37,7 +55,11 @@ b.setBounds (10, 10, 200, 200); b.addListener (SWT.Selection, new class() Listener{ public void handleEvent (Event e) { - b.setCursor (cursor); + //b.setCursor (cursor); + auto job = new MyJob("test"); + job.schedule(); + job.join; + Stdout.formatln("job done"); } }); while (!shell.isDisposed ()) { diff -r f05e6e8b2f2d -r 0a55d2d5a946 rakefile --- a/rakefile Sun Apr 12 12:27:13 2009 +0200 +++ b/rakefile Tue Apr 14 11:35:29 2009 +0200 @@ -318,6 +318,11 @@ buildTree( "org.eclipse.core.jobs", "src", "res" ) end +desc "Build Eclipse Core Databinding" +task :corebind do + buildTree( "org.eclipse.core.databinding", "src", "res" ) +end + desc "Build JFace" task :jface do buildTree( "org.eclipse.jface", "src", "res" ) @@ -335,6 +340,11 @@ buildTree( "org.eclipse.jface.text", "src", "res" ) end +desc "Build JFace.Databinding" +task :jfacebind do + buildTree( "org.eclipse.jface.databinding", "src", "res" ) +end + desc "Build UI Forms" task :uiforms do buildTree( "org.eclipse.ui.forms", "src", "res" ) diff -r f05e6e8b2f2d -r 0a55d2d5a946 refactors.txt --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/refactors.txt Tue Apr 14 11:35:29 2009 +0200 @@ -0,0 +1,22 @@ +set noignorecase +bufdo retab +bufdo set ff=unix +bufdo %s:==:is:g +bufdo %s:!=:!is:g + +bufdo %s:\:bool:g +bufdo %s:^\(\s*\(public\|private\|protected\|\)\s*\)\([A-Z]\i*\)\(\s*(\):\1this\4: +bufdo %s:\:,:gc +bufdo %s:\:\::gc +bufdo %s:\:\::gc +bufdo %s:\:this.outer:g + gg/package cemoduleo%€kb€kb;bhvlkJf;bhvF;xi.o import java.lang.all; +bufdo %s:(\s*\(byte\|short\|int\|long\|float\|double\|[A-Z]\i*\(\.[A-Z]\i*\)\?\)\s*\(\[\s*]\s*\)\?):cast\0:g + +bufdo %s:\([(,|&=]\)\_s*\(\i\+\)\_s\+instanceof\_s\+\([A-Z]\i*\(\.[A-Z]\i*\)\?\s*\(\[\s*]\s*\)\?\)\(\_s*[),|&?]\):\1 null !is cast(\3)\2 \6:g +bufdo %s:\:ClassInfo:gc +bufdo %s:\.class\>:.classinfo:gc +bufdo %s:\(\\s*\)\\(\s*(\):override \1opEquals\2:c +bufdo %s:\()\_s*\)throws\_s\+[A-Z]\i*\_s*\(,\_s*[A-Z]\i*\_s*\)*{:\1{: +