# HG changeset patch
# User Frank Benoit
+ * Subclasses may extend, but must call the super implementation.
+ *
+ * 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.
+ *
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
+ * 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}).
+ *
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}).
+ *
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. Returnsnull
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+ * The update phases are: + *
+ * 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}).
+ *
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. + * Returnsnull
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 providednewStats
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 providedfromObject
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 @@
+
+
+
+
+
+
+ +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; i
+ * 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}.
+ *
+ * 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.
+ * 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.
+ *
+ *
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, ornull
+ * 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.
+ *
+ * - {@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.
+ *
- {@link ListDiffVisitor#handleMove(int, int, Object)} is called
+ * whenever a remove entry is immediately followed by an add entry with an
+ * equivalent element.
+ *
- {@link ListDiffVisitor#handleRemove(int, Object)} is called whenever
+ * a remove entry does not match conditions 1 or 2.
+ *
- {@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 /*