changeset 78:0a55d2d5a946

Added file for databinding
author Frank Benoit <benoit@tionex.de>
date Tue, 14 Apr 2009 11:35:29 +0200
parents f05e6e8b2f2d
children 635be320a5da
files base/src/java/lang/Thread.d base/src/java/util/HashSet.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/AggregateValidationStatus.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/Binding.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingException.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/DataBindingContext.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/ListBinding.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/ObservablesManager.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/SetBinding.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateListStrategy.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateSetStrategy.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateValueStrategy.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValidationStatusProvider.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValueBinding.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/Converter.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/IConverter.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/NumberToStringConverter.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/StringToNumberConverter.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/conversion/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/AbstractObservable.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeManager.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ChangeSupport.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Diffs.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IChangeListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservable.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservableCollection.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservablesListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObserving.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IStaleListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ObservableEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/ObservableTracker.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Observables.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/Realm.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/StaleEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/AbstractObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ComputedList.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/IListChangeListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/IObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListChangeEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiff.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiffEntry.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ListDiffVisitor.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/ObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/WritableList.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/list/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/AbstractObservableMap.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/BidirectionalMap.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/CompositeMap.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/ComputedObservableMap.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/IMapChangeListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/IObservableMap.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/MapChangeEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/MapDiff.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/ObservableMap.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/WritableMap.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/map/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/IObservableFactory.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/MasterDetailObservables.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/masterdetail/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/AbstractObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/IObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ISetChangeListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ListToSetAdapter.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/MappedSet.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/ObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/SetChangeEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/SetDiff.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/UnionSet.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/WritableSet.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/set/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/AbstractObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/AbstractVetoableValue.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ChangeVetoException.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ComputedValue.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IValueChangeListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IValueChangingListener.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/IVetoableValue.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueChangeEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueChangingEvent.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/ValueDiff.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/WritableValue.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/value/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/ILogger.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/Policy.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/util/package.html org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/IValidator.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/MultiValidator.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/ValidationStatus.d org.eclipse.core.databinding/src/org/eclipse/core/databinding/validation/package.html org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Activator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingMessages.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/BindingStatus.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ClassLookupSupport.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/IdentityWrapper.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Pair.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Queue.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/RandomAccessListIterator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/Util.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/ValidationStatusMap.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/CharacterToStringConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateConversionSupport.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/DateToStringConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IdentityConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/IntegerToStringConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigDecimalConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToBigIntegerConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToByteConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToDoubleConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToFloatConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToIntegerConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToLongConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToNumberConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/NumberToShortConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/ObjectToStringConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StatusToStringConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToBooleanPrimitiveConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToByteConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToCharacterConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToDateConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToNumberParser.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/conversion/StringToShortConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/messages.properties org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ConstantObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/EmptyObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/EmptyObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/IStalenessConsumer.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/MapEntryObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ProxyObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ProxyObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/StalenessObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/StalenessTracker.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/UnmodifiableObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableMap.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/ValidatedObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableList.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableMap.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableSet.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/masterdetail/DetailObservableValue.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/AbstractObservableTree.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IChildrenCountUpdate.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IChildrenUpdate.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IHasChildrenUpdate.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IObservableTree.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IOrderedTreeProvider.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/ITreeChangeListener.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IUnorderedTreeProvider.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/IViewerUpdate.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeChangeEvent.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiff.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiffNode.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreeDiffVisitor.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/TreePath.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/observable/tree/package.html org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/AbstractStringToNumberValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberFormatConverter.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToByteValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToDoubleValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToFloatValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToIntegerValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToLongValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToNumberValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToShortValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/NumberToUnboundedNumberValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ObjectToPrimitiveValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/ReadOnlyValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToByteValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToCharacterValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDateValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToDoubleValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToFloatValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToIntegerValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToLongValidator.d org.eclipse.core.databinding/src/org/eclipse/core/internal/databinding/validation/StringToShortValidator.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/JobOSGiUtils.d org.eclipse.core.jobs/src/org/eclipse/core/internal/jobs/Semaphore.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservable.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/ISWTObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/SWTObservables.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/swt/package.html org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservable.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableList.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableSet.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/IViewerObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ListeningLabelProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListContentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableListTreeContentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableMapLabelProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetContentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableSetTreeContentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ObservableValueEditingSupport.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/TreeStructureAdvisor.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/ViewersObservables.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/viewers/package.html org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/WizardPageSupport.d org.eclipse.jface.databinding/src/org/eclipse/jface/databinding/wizard/package.html org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/internal/swt/LinkObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/AbstractSWTObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/AbstractSWTVetoableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/CompositeUpdater.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/ControlUpdater.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/MenuUpdater.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/SWTUtil.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/TableUpdater.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/swt/WorkQueue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/IParentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/TreeNode.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/UnorderedTreeContentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/provisional/viewers/ViewerLabelProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ButtonObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboObservableList.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CComboSingleSelectionObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/CLabelObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboObservableList.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ComboSingleSelectionObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ControlObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/DelayedObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/LabelObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListObservableList.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ListSingleSelectionObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTObservableList.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SWTProperties.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ScaleObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/ShellObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SingleSelectionObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/SpinnerObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TableSingleSelectionObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextEditableObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/swt/TextObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckableCheckedElementsObservableSet.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/CheckboxViewerCheckedElementsObservableSet.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/LeafNodesSet.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ListViewerUpdater.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionContentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableCollectionTreeContentProvider.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableViewerElementSet.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderMultipleSelectionObservableList.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderSingleSelectionObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TableViewerUpdater.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TreeViewerUpdater.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementMap.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementSet.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementWrapper.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerInputObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerMultipleSelectionObservableList.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerSingleSelectionObservableValue.d org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerUpdater.d org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.d rakefile refactors.txt
diffstat 262 files changed, 30984 insertions(+), 20 deletions(-) [+]
line wrap: on
line diff
--- a/base/src/java/lang/Thread.d	Sun Apr 12 12:27:13 2009 +0200
+++ b/base/src/java/lang/Thread.d	Tue Apr 14 11:35:29 2009 +0200
@@ -17,7 +17,7 @@
     }
     private TThread thread;
     private Runnable runnable;
-
+    private bool interrupted_ = false;
     version(Tango){
         private alias tango.core.Thread.ThreadLocal!(Thread) TTLS;
         private static TTLS tls;
@@ -97,7 +97,7 @@
 //         assert( MIN_PRIORITY < MAX_PRIORITY );
 //         assert( tango.core.Thread.Thread.PRIORITY_MIN < tango.core.Thread.Thread.PRIORITY_MAX );
         auto scaledPrio = (newPriority-MIN_PRIORITY) * (TThread.PRIORITY_MAX-TThread.PRIORITY_MIN) / (MAX_PRIORITY-MIN_PRIORITY) +TThread.PRIORITY_MIN;
-        getDwtLogger().trace( __FILE__, __LINE__, "Thread.setPriority: scale ({} {} {}) -> ({} {} {})", MIN_PRIORITY, newPriority, MAX_PRIORITY, TThread.PRIORITY_MIN, scaledPrio, TThread.PRIORITY_MAX);
+//        getDwtLogger().trace( __FILE__, __LINE__, "Thread.setPriority: scale ({} {} {}) -> ({} {} {})", MIN_PRIORITY, newPriority, MAX_PRIORITY, TThread.PRIORITY_MIN, scaledPrio, TThread.PRIORITY_MAX);
 //         thread.priority( scaledPrio );
     }
 
@@ -140,12 +140,17 @@
     }
 
     void interrupt() {
+        interrupted_ = true;
         implMissing(__FILE__,__LINE__);
     }
 
     static bool interrupted() {
-        implMissing(__FILE__,__LINE__);
-        return false;
+        auto t = currentThread();
+        synchronized(t){
+            bool res = t.interrupted_;
+            t.interrupted_ = false;
+            return res;
+        }
     }
 
     public void run(){
--- a/base/src/java/util/HashSet.d	Sun Apr 12 12:27:13 2009 +0200
+++ b/base/src/java/util/HashSet.d	Tue Apr 14 11:35:29 2009 +0200
@@ -25,13 +25,26 @@
         }
     }
     public this(Collection c){
-        implMissing( __FILE__, __LINE__ );
+        version(Tango){
+            set = new SetType();
+            addAll(c);
+        } else { // Phobos
+            implMissing( __FILE__, __LINE__ );
+        }
     }
     public this(int initialCapacity){
-        implMissing( __FILE__, __LINE__ );
+        version(Tango){
+            set = new SetType();
+        } else { // Phobos
+            implMissing( __FILE__, __LINE__ );
+        }
     }
     public this(int initialCapacity, float loadFactor){
-        implMissing( __FILE__, __LINE__ );
+        version(Tango){
+            set = new SetType(loadFactor);
+        } else { // Phobos
+            implMissing( __FILE__, __LINE__ );
+        }
     }
     public bool    add(Object o){
         version(Tango){
@@ -45,8 +58,11 @@
         return add(stringcast(o));
     }
     public bool    addAll(Collection c){
-        implMissing( __FILE__, __LINE__ );
-        return false;
+        bool res = false;
+        foreach( o; c ){
+            res |= add(o);
+        }
+        return res;
     }
     public void   clear(){
         version(Tango){
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/AggregateValidationStatus.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,262 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Matt Carter - bug 182822
+ *     Boris Bokowski - bug 218269
+ *     Matthew Hall - bug 218269
+ *******************************************************************************/
+module org.eclipse.core.databinding.AggregateValidationStatus;
+
+import java.lang.all;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.IChangeListener;
+import org.eclipse.core.databinding.observable.IObservableCollection;
+import org.eclipse.core.databinding.observable.IStaleListener;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.ComputedValue;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.BindingMessages;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * This class can be used to aggregate status values from a data binding context
+ * into a single status value. Instances of this class can be used as an
+ * observable value with a value type of {@link IStatus}, or the static methods
+ * can be called directly if an aggregated status result is only needed once.
+ * 
+ * @since 1.0
+ * 
+ */
+public final class AggregateValidationStatus : IObservableValue {
+
+    private IObservableValue implementation;
+
+    /**
+     * Constant denoting an aggregation strategy that merges multiple non-OK
+     * status objects in a {@link MultiStatus}. Returns an OK status result if
+     * all statuses from the given validation status providers are the an OK
+     * status. Returns a single status if there is only one non-OK status.
+     * 
+     * @see #getStatusMergedcast(Collection)
+     */
+    public static final int MERGED = 1;
+
+    /**
+     * Constant denoting an aggregation strategy that always returns the most
+     * severe status from the given validation status providers. If there is
+     * more than one status at the same severity level, it picks the first one
+     * it encounters.
+     * 
+     * @see #getStatusMaxSeveritycast(Collection)
+     */
+    public static final int MAX_SEVERITY = 2;
+
+    /**
+     * Creates a new aggregate validation status observable for the given data
+     * binding context.
+     * 
+     * @param dbc
+     *            a data binding context
+     * @param strategy
+     *            a strategy constant, one of {@link #MERGED} or
+     *            {@link #MAX_SEVERITY}.
+     * @since 1.1
+     */
+    public this(DataBindingContext dbc, int strategy) {
+        this(dbc.getValidationRealm(), dbc.getValidationStatusProviders(),
+                strategy);
+    }
+
+    /**
+     * @param validationStatusProviders
+     *            an observable collection containing elements of type
+     *            {@link ValidationStatusProvider}
+     * @param strategy
+     *            a strategy constant, one of {@link #MERGED} or
+     *            {@link #MAX_SEVERITY}.
+     * @see DataBindingContext#getValidationStatusProviders()
+     */
+    public this(
+            IObservableCollection validationStatusProviders, strategy) {
+        this(Realm.getDefault(), validationStatusProviders, strategy);
+    }
+
+    /**
+     * @param realm
+     *            Realm
+     * @param validationStatusProviders
+     *            an observable collection containing elements of type
+     *            {@link ValidationStatusProvider}
+     * @param strategy
+     *            a strategy constant, one of {@link #MERGED} or
+     *            {@link #MAX_SEVERITY}.
+     * @see DataBindingContext#getValidationStatusProviders()
+     * @since 1.1
+     */
+    public this(Realm realm,
+            IObservableCollection validationStatusProviders, int strategy) {
+        if (strategy is MERGED) {
+            implementation = new class(realm, IStatus.classinfo, validationStatusProviders) ComputedValue {
+                IObservableCollection validationStatusProviders_;
+                this(Realm r, ClassInfo c, IObservableCollection v){
+                    super(r, c);
+                    validationStatusProviders_=v;
+                }
+                protected Object calculate() {
+                    return getStatusMerged(validationStatusProviders_);
+                }
+            };
+        } else {
+            implementation = new class(realm, IStatus.classinfo, validationStatusProviders) ComputedValue {
+                IObservableCollection validationStatusProviders_;
+                this(Realm r, ClassInfo c, IObservableCollection v){
+                    super(r, c);
+                    validationStatusProviders_=v;
+                }
+                protected Object calculate() {
+                    return getStatusMaxSeverity(validationStatusProviders_);
+                }
+            };
+        }
+    }
+
+    /**
+     * @param listener
+     * @see org.eclipse.core.databinding.observable.IObservable#addChangeListener(org.eclipse.core.databinding.observable.IChangeListener)
+     */
+    public void addChangeListener(IChangeListener listener) {
+        implementation.addChangeListener(listener);
+    }
+
+    /**
+     * @param listener
+     * @see org.eclipse.core.databinding.observable.IObservable#addStaleListener(org.eclipse.core.databinding.observable.IStaleListener)
+     */
+    public void addStaleListener(IStaleListener listener) {
+        implementation.addStaleListener(listener);
+    }
+
+    /**
+     * @param listener
+     * @see org.eclipse.core.databinding.observable.value.IObservableValue#addValueChangeListener(org.eclipse.core.databinding.observable.value.IValueChangeListener)
+     */
+    public void addValueChangeListener(IValueChangeListener listener) {
+        implementation.addValueChangeListener(listener);
+    }
+
+    public void dispose() {
+        implementation.dispose();
+    }
+
+    public Realm getRealm() {
+        return implementation.getRealm();
+    }
+
+    public Object getValue() {
+        return implementation.getValue();
+    }
+
+    public Object getValueType() {
+        return implementation.getValueType();
+    }
+
+    public bool isStale() {
+        return implementation.isStale();
+    }
+
+    public void removeChangeListener(IChangeListener listener) {
+        implementation.removeChangeListener(listener);
+    }
+
+    public void removeStaleListener(IStaleListener listener) {
+        implementation.removeStaleListener(listener);
+    }
+
+    public void removeValueChangeListener(IValueChangeListener listener) {
+        implementation.removeValueChangeListener(listener);
+    }
+
+    public void setValue(Object value) {
+        implementation.setValue(value);
+    }
+
+    /**
+     * Returns a status object that merges multiple non-OK status objects in a
+     * {@link MultiStatus}. Returns an OK status result if all statuses from
+     * the given validation status providers are the an OK status. Returns a
+     * single status if there is only one non-OK status.
+     * 
+     * @param validationStatusProviders
+     *            a collection of validation status providers
+     * @return a merged status
+     */
+    public static IStatus getStatusMerged(Collection validationStatusProviders) {
+        List statuses = new ArrayList();
+        for (Iterator it = validationStatusProviders.iterator(); it.hasNext();) {
+            ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) it
+                    .next();
+            IStatus status = cast(IStatus) validationStatusProvider
+                    .getValidationStatus().getValue();
+            if (!status.isOK()) {
+                statuses.add(status);
+            }
+        }
+        if (statuses.size() is 1) {
+            return cast(IStatus) statuses.get(0);
+        }
+        if (!statuses.isEmpty()) {
+            MultiStatus result = new MultiStatus(Policy.JFACE_DATABINDING, 0,
+                    BindingMessages
+                            .getStringcast(BindingMessages.MULTIPLE_PROBLEMS), null);
+            for (Iterator it = statuses.iterator(); it.hasNext();) {
+                IStatus status = cast(IStatus) it.next();
+                result.merge(status);
+            }
+            return result;
+        }
+        return Status.OK_STATUS;
+    }
+
+    /**
+     * Returns a status that always returns the most severe status from the
+     * given validation status providers. If there is more than one status at
+     * the same severity level, it picks the first one it encounters.
+     * 
+     * @param validationStatusProviders
+     *            a collection of validation status providers
+     * @return a single status reflecting the most severe status from the given
+     *         validation status providers
+     */
+    public static IStatus getStatusMaxSeverity(
+            Collection validationStatusProviders) {
+        int maxSeverity = IStatus.OK;
+        IStatus maxStatus = Status.OK_STATUS;
+        for (Iterator it = validationStatusProviders.iterator(); it.hasNext();) {
+            ValidationStatusProvider validationStatusProvider = cast(ValidationStatusProvider) it
+                    .next();
+            IStatus status = cast(IStatus) validationStatusProvider
+                    .getValidationStatus().getValue();
+            if (status.getSeverity() > maxSeverity) {
+                maxSeverity = status.getSeverity();
+                maxStatus = status;
+            }
+        }
+        return maxStatus;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/Binding.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,165 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Brad Reynolds - bug 159768
+ *     Boris Bokowski - bug 218269
+ *     Matthew Hall - bug 218269
+ *******************************************************************************/
+
+module org.eclipse.core.databinding.Binding;
+
+import java.lang.all;
+
+import java.util.Collections;
+
+import org.eclipse.core.databinding.observable.IObservable;
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+
+/**
+ * This abstract class represents a binding between a model and a target. Newly
+ * created instances need to be added to a data binding context using
+ * {@link #initcast(DataBindingContext)}.
+ * 
+ * @since 1.0
+ */
+public abstract class Binding : ValidationStatusProvider {
+
+    protected DataBindingContext context;
+    private IObservable target;
+    private IObservable model;
+    
+    /**
+     * Creates a new binding.
+     * 
+     * @param target target observable
+     * @param model model observable
+     */
+    public this(IObservable target, IObservable model) {
+        this.target = target;
+        this.model = model;
+    }
+    
+    /**
+     * Initializes this binding with the given context and adds it to the list
+     * of bindings of the context.
+     * <p>
+     * Subclasses may extend, but must call the super implementation.
+     * </p>
+     * 
+     * @param context
+     */
+    public final void init(DataBindingContext context) {
+        this.context = context;
+        preInit();
+        context.addBinding(this);
+        postInit();
+    }
+    
+    /**
+     * Called by {@link #initcast(DataBindingContext)} after setting
+     * {@link #context} but before adding this binding to the context.
+     * Subclasses may use this method to perform initialization that could not
+     * be done in the constructor. Care should be taken not to cause any events
+     * while running this method.
+     */
+    protected abstract void preInit();
+    
+    /**
+     * Called by {@link #initcast(DataBindingContext)} after adding this binding to
+     * the context. Subclasses may use this method to perform initialization
+     * that may cause events to be fired, including BindingEvents that are
+     * forwarded to the data binding context.
+     */
+    protected abstract void postInit();
+
+    /**
+     * @return an observable value containing the current validation status
+     */
+    public abstract IObservableValue getValidationStatus();
+
+    /**
+     * Updates the model's state from the target's state at the next reasonable
+     * opportunity. There is no guarantee that the state will have been updated
+     * by the time this call returns.
+     */
+    public abstract void updateTargetToModel();
+
+    /**
+     * Updates the target's state from the model's state at the next reasonable
+     * opportunity. There is no guarantee that the state will have been updated
+     * by the time this call returns.
+     */
+    public abstract void updateModelToTarget();
+    
+    /**
+     * Validates the target's state at the next reasonable
+     * opportunity. There is no guarantee that the validation status will have been updated
+     * by the time this call returns.
+     */
+    public abstract void validateTargetToModel();
+    
+    /**
+     * Validates the model's state at the next reasonable
+     * opportunity. There is no guarantee that the validation status will have been updated
+     * by the time this call returns.
+     */
+    public abstract void validateModelToTarget();
+    
+    /**
+     * Disposes of this Binding. Subclasses may extend, but must call super.dispose().
+     */
+    public void dispose() {
+        if (context !is null) {
+            context.removeBinding(this);
+        }
+        context = null;
+        target = null;
+        model = null;
+        super.dispose();
+    }
+
+    /**
+     * @param context
+     */
+    /* package */ void setDataBindingContext(DataBindingContext context) {
+        this.context = context;
+    }
+
+    /**
+     * @return target observable
+     */
+    public IObservable getTarget() {
+        return target;
+    }
+
+    /**
+     * @since 1.1
+     */
+    public IObservableList getTargets() {
+        return Observables.staticObservableList(context.getValidationRealm(),
+                Collections.singletonList(target));
+    }
+
+    /**
+     * @return model observable
+     */
+    public IObservable getModel() {
+        return model;
+    }
+
+    /**
+     * @since 1.1
+     */
+    public IObservableList getModels() {
+        return Observables.staticObservableList(context.getValidationRealm(),
+                Collections.singletonList(model));
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/BindingException.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,66 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2007 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module org.eclipse.core.databinding.BindingException;
+
+import java.lang.all;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * An unchecked exception indicating a binding problem.
+ * 
+ * @since 1.0
+ */
+public class BindingException : RuntimeException {
+
+    /*
+     * Needed because all Throwables are Serializable.
+     */
+    private static final long serialVersionUID = -4092828452936724217L;
+    private Throwable cause;
+
+    /**
+     * Creates a new BindingException with the given message.
+     * 
+     * @param message
+     */
+    public this(String message) {
+        super(message);
+    }
+
+    /**
+     * Creates a new BindingException with the given message and cause.
+     * 
+     * @param message
+     * @param cause
+     */
+    public this(String message, Throwable cause) {
+        super(message);
+        this.cause = cause;
+    }
+
+    public void printStackTrace(PrintStream err) {
+        super.printStackTrace(err);
+        if (cause !is null) {
+            err.println("caused by:"); //$NON-NLS-1$
+            cause.printStackTrace(err);
+        }
+    }
+
+    public void printStackTrace(PrintWriter err) {
+        super.printStackTrace(err);
+        if (cause !is null) {
+            err.println("caused by:"); //$NON-NLS-1$
+            cause.printStackTrace(err);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/DataBindingContext.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,434 @@
+/*******************************************************************************
+ * Copyright (c) 2005-2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Brad Reynolds - bug 159539
+ *     Brad Reynolds - bug 140644
+ *     Brad Reynolds - bug 159940
+ *     Brad Reynolds - bug 116920, 159768
+ *     Matthew Hall - bugs 118516, 124684, 218269
+ *     Boris Bokowski - bug 218269
+ *******************************************************************************/
+module org.eclipse.core.databinding.DataBindingContext;
+
+import java.lang.all;
+
+import java.util.Iterator;
+
+import org.eclipse.core.databinding.observable.Observables;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.core.databinding.observable.map.IObservableMap;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.internal.databinding.ValidationStatusMap;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.IStatus;
+
+/**
+ * A DataBindingContext is the point of contact for the creation and management
+ * of {@link Binding bindings}, and aggregates validation statuses of its
+ * bindings, or more generally, its validation status providers.
+ * <p>
+ * A DataBindingContext provides the following abilities:
+ * <ul>
+ * <li>Ability to create bindings between
+ * {@link IObservableValue observable values}.</li>
+ * <li>Ability to create bindings between
+ * {@link IObservableList observable lists}.</li>
+ * <li>Access to the bindings created by the instance.</li>
+ * <li>Access to the list of validation status providers (this includes all
+ * bindings).</li>
+ * </ul>
+ * </p>
+ * <p>
+ * 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 <code>IWizardPage</code> could use a single context and the statuses
+ * could be aggregated to set the page status and fulfillment. Each page in the
+ * <code>IWizard</code> would have its own context instance.
+ * </p>
+ * 
+ * @since 1.0
+ */
+public class DataBindingContext {
+    private WritableList bindings;
+    private WritableList validationStatusProviders;
+
+    /**
+     * Unmodifiable version of {@link #bindings} for public exposure.
+     */
+    private IObservableList unmodifiableBindings;
+    /**
+     * Unmodifiable version of {@link #validationStatusProviders} for public
+     * exposure.
+     */
+    private IObservableList unmodifiableStatusProviders;
+
+    private IObservableMap validationStatusMap;
+
+    private Realm validationRealm;
+
+    /**
+     * Creates a data binding context, using the current default realm for the
+     * validation observables.
+     * 
+     * @see Realm
+     */
+    public this() {
+        this(Realm.getDefault());
+    }
+
+    /**
+     * Creates a data binding context using the given realm for the validation
+     * observables.
+     * 
+     * @param validationRealm
+     *            the realm to be used for the validation observables
+     * 
+     * @see Realm
+     */
+    public this(Realm validationRealm) {
+        Assert.isNotNull(validationRealm, "Validation realm cannot be null"); //$NON-NLS-1$
+        this.validationRealm = validationRealm;
+
+        bindings = new WritableList(validationRealm);
+        unmodifiableBindings = Observables.unmodifiableObservableList(bindings);
+
+        validationStatusProviders = new WritableList(validationRealm);
+        unmodifiableStatusProviders = Observables
+                .unmodifiableObservableList(validationStatusProviders);
+
+        validationStatusMap = new ValidationStatusMap(validationRealm, bindings);
+    }
+
+    /**
+     * Creates a {@link Binding} to synchronize the values of two
+     * {@link IObservableValue observable values}. During synchronization
+     * validation and conversion can be employed to customize the process. For
+     * specifics on the customization of the process see
+     * {@link UpdateValueStrategy}.
+     * 
+     * @param targetObservableValue
+     *            target value, commonly a UI widget
+     * @param modelObservableValue
+     *            model value
+     * @param targetToModel
+     *            strategy to employ when the target is the source of the change
+     *            and the model is the destination
+     * @param modelToTarget
+     *            strategy to employ when the model is the source of the change
+     *            and the target is the destination
+     * @return created binding
+     * 
+     * @see UpdateValueStrategy
+     */
+    public final Binding bindValue(IObservableValue targetObservableValue,
+            IObservableValue modelObservableValue,
+            UpdateValueStrategy targetToModel, UpdateValueStrategy modelToTarget) {
+        UpdateValueStrategy targetToModelStrategy = targetToModel !is null ? targetToModel
+                        : createTargetToModelUpdateValueStrategy(targetObservableValue, modelObservableValue);
+        UpdateValueStrategy modelToTargetStrategy = modelToTarget !is null ? modelToTarget
+                : createModelToTargetUpdateValueStrategy(modelObservableValue, targetObservableValue);
+        targetToModelStrategy.fillDefaults(targetObservableValue, modelObservableValue);
+        modelToTargetStrategy.fillDefaults(modelObservableValue, targetObservableValue);
+        ValueBinding result = new ValueBinding(targetObservableValue,
+                modelObservableValue, targetToModelStrategy,
+                modelToTargetStrategy);
+        result.init(this);
+        return result;
+    }
+
+    /**
+     * Returns an update value strategy to be used for copying values from the
+     * from value to the to value. Clients may override.
+     * 
+     * @param fromValue
+     * @param toValue
+     * @return a update value strategy
+     */
+    protected UpdateValueStrategy createModelToTargetUpdateValueStrategy(
+            IObservableValue fromValue, IObservableValue toValue) {
+        return new UpdateValueStrategy();
+    }
+
+    /**
+     * Returns an update value strategy to be used for copying values from the
+     * from value to the to value. Clients may override.
+     * 
+     * @param fromValue
+     * @param toValue
+     * @return a update value strategy
+     */
+    protected UpdateValueStrategy createTargetToModelUpdateValueStrategy(
+            IObservableValue fromValue, IObservableValue toValue) {
+        return new UpdateValueStrategy();
+    }
+    
+    /**
+     * Creates a {@link Binding} to synchronize the values of two
+     * {@link IObservableList observable lists}. During synchronization
+     * validation and conversion can be employed to customize the process. For
+     * specifics on the customization of the process see
+     * {@link UpdateListStrategy}.
+     * 
+     * @param targetObservableList
+     *            target list, commonly a list representing a list in the UI
+     * @param modelObservableList
+     *            model list
+     * @param targetToModel
+     *            strategy to employ when the target is the source of the change
+     *            and the model is the destination
+     * @param modelToTarget
+     *            strategy to employ when the model is the source of the change
+     *            and the target is the destination
+     * @return created binding
+     * 
+     * @see UpdateListStrategy
+     */
+    public final Binding bindList(IObservableList targetObservableList,
+            IObservableList modelObservableList,
+            UpdateListStrategy targetToModel, UpdateListStrategy modelToTarget) {
+        UpdateListStrategy targetToModelStrategy = targetToModel !is null ? targetToModel
+                : createTargetToModelUpdateListStrategy(targetObservableList,
+                        modelObservableList);
+        UpdateListStrategy modelToTargetStrategy = modelToTarget !is null ? modelToTarget
+                : createModelToTargetUpdateListStrategy(modelObservableList,
+                        targetObservableList);
+        targetToModelStrategy.fillDefaults(targetObservableList,
+                modelObservableList);
+        modelToTargetStrategy.fillDefaults(modelObservableList,
+                targetObservableList);
+        ListBinding result = new ListBinding(targetObservableList,
+                modelObservableList, targetToModelStrategy,
+                modelToTargetStrategy);
+        result.init(this);
+        return result;
+    }
+
+    /**
+     * @param modelObservableList
+     * @param targetObservableList
+     * @return an update list strategy
+     */
+    protected UpdateListStrategy createModelToTargetUpdateListStrategy(
+            IObservableList modelObservableList,
+            IObservableList targetObservableList) {
+        return new UpdateListStrategy();
+    }
+
+    /**
+     * @param targetObservableList
+     * @param modelObservableList
+     * @return an update list strategy 
+     */
+    protected UpdateListStrategy createTargetToModelUpdateListStrategy(
+            IObservableList targetObservableList,
+            IObservableList modelObservableList) {
+        return new UpdateListStrategy();
+    }
+
+    /**
+     * Creates a {@link Binding} to synchronize the values of two
+     * {@link IObservableSet observable sets}. During synchronization
+     * validation and conversion can be employed to customize the process. For
+     * specifics on the customization of the process see
+     * {@link UpdateSetStrategy}.
+     * 
+     * @param targetObservableSet
+     *            target set, commonly a set representing a set in the UI
+     * @param modelObservableSet
+     *            model set
+     * @param targetToModel
+     *            strategy to employ when the target is the source of the change
+     *            and the model is the destination
+     * @param modelToTarget
+     *            strategy to employ when the model is the source of the change
+     *            and the target is the destination
+     * @return created binding
+     * @since 1.1
+     */
+    public final Binding bindSet(IObservableSet targetObservableSet,
+            IObservableSet modelObservableSet, UpdateSetStrategy targetToModel,
+            UpdateSetStrategy modelToTarget) {
+        if (targetToModel is null)
+            targetToModel = createTargetToModelUpdateSetStrategy(
+                    targetObservableSet, modelObservableSet);
+        if (modelToTarget is null)
+            modelToTarget = createModelToTargetUpdateSetStrategy(
+                    modelObservableSet, targetObservableSet);
+        targetToModel.fillDefaults(targetObservableSet, modelObservableSet);
+        modelToTarget.fillDefaults(modelObservableSet, targetObservableSet);
+        SetBinding result = new SetBinding(targetObservableSet,
+                modelObservableSet, targetToModel, modelToTarget);
+        result.init(this);
+        return result;
+    }
+
+    /**
+     * @param targetObservableSet 
+     * @param modelObservableSet 
+     * @return a default set update strategy
+     * @since 1.1
+     */
+    protected UpdateSetStrategy createTargetToModelUpdateSetStrategy(
+            IObservableSet targetObservableSet,
+            IObservableSet modelObservableSet) {
+        return new UpdateSetStrategy();
+    }
+
+    /**
+     * @param modelObservableSet 
+     * @param targetObservableSet 
+     * @return a default set update strategy 
+     * @since 1.1
+     */
+    protected UpdateSetStrategy createModelToTargetUpdateSetStrategy(
+            IObservableSet modelObservableSet,
+            IObservableSet targetObservableSet) {
+        return new UpdateSetStrategy();
+    }
+
+    /**
+     * Disposes of this data binding context and all bindings and validation
+     * status providers that were added to this context.
+     */
+    public final void dispose() {
+        Binding[] bindingArray = cast(Binding[]) bindings.toArray(new Binding[bindings.size()]);
+        for (int i = 0; i < bindingArray.length; i++) {
+            bindingArray[i].dispose();
+        }
+        ValidationStatusProvider[] statusProviderArray = cast(ValidationStatusProvider[]) validationStatusProviders
+                .toArray(new ValidationStatusProvider[validationStatusProviders
+                        .size()]);
+        for (int i = 0; i < statusProviderArray.length; i++) {
+            if (!statusProviderArray[i].isDisposed())
+                statusProviderArray[i].dispose();
+        }
+    }
+
+    /**
+     * Returns an unmodifiable observable list with elements of type
+     * {@link Binding}, ordered by time of addition.
+     * 
+     * @return the observable list containing all bindings
+     */
+    public final IObservableList getBindings() {
+        return unmodifiableBindings;
+    }
+
+    /**
+     * Returns an unmodifiable observable list with elements of type
+     * {@link ValidationStatusProvider}, ordered by time of addition.
+     * 
+     * @return the observable list containing all bindings
+     * @since 1.1
+     */
+    public final IObservableList getValidationStatusProviders() {
+        return unmodifiableStatusProviders;
+    }
+
+    /**
+     * Returns an observable map from bindings (type: {@link Binding}) to
+     * statuses (type: {@link IStatus}). The keys of the map are the bindings
+     * returned by {@link #getBindings()}, and the values are the current
+     * validaion status objects for each binding.
+     * 
+     * @return the observable map from bindings to status objects.
+     * 
+     * @deprecated as of 1.1, please use {@link #getValidationStatusProviders()}
+     */
+    public final IObservableMap getValidationStatusMap() {
+        return validationStatusMap;
+    }
+
+    /**
+     * Adds the given binding to this data binding context. This will also add
+     * the given binding to the list of validation status providers.
+     * 
+     * @param binding
+     *            The binding to add.
+     * @see #addValidationStatusProvidercast(ValidationStatusProvider)
+     * @see #getValidationStatusProviders()
+     */
+    public void addBinding(Binding binding) {
+        addValidationStatusProvider(binding);
+        bindings.add(binding);
+    }
+
+    /**
+     * Adds the given validation status provider to this data binding context.
+     * 
+     * @param validationStatusProvider
+     *            The validation status provider to add.
+     * @since 1.1
+     */
+    public void addValidationStatusProvider(
+            ValidationStatusProvider validationStatusProvider) {
+        validationStatusProviders.add(validationStatusProvider);
+    }
+
+    /**
+     * Updates all model observable objects to reflect the current state of the
+     * target observable objects.
+     * 
+     */
+    public final void updateModels() {
+        for (Iterator it = bindings.iterator(); it.hasNext();) {
+            Binding binding = cast(Binding) it.next();
+            binding.updateTargetToModel();
+        }
+    }
+
+    /**
+     * Updates all target observable objects to reflect the current state of the
+     * model observable objects.
+     * 
+     */
+    public final void updateTargets() {
+        for (Iterator it = bindings.iterator(); it.hasNext();) {
+            Binding binding = cast(Binding) it.next();
+            binding.updateModelToTarget();
+        }
+    }
+
+    /**
+     * Removes the given binding.
+     * 
+     * @param binding
+     * @return <code>true</code> if was associated with the context,
+     *         <code>false</code> if not
+     */
+    public bool removeBinding(Binding binding) {
+        return bindings.remove(binding) && removeValidationStatusProvider(binding);
+    }
+
+    /**
+     * Removes the validation status provider.
+     * 
+     * @param validationStatusProvider
+     * @return <code>true</code> if was associated with the context,
+     *         <code>false</code> 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;
+    }
+}
--- /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 <code>newStatus</code> into the
+     * <code>multiStatus</code>.
+     * 
+     * @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();
+    }
+}
--- /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
+     *            <code>true</code> if the target observables of the context
+     *            should be managed
+     * @param trackModels
+     *            <code>true</code> 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();
+        }
+    }
+}
--- /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 <code>newStatus</code> into the
+     * <code>multiStatus</code>.
+     * 
+     * @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();
+    }
+}
--- /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<tom.schindl@bestsolution.at> - bugfix for 217940
+ *******************************************************************************/
+
+module org.eclipse.core.databinding.UpdateListStrategy;
+
+import java.lang.all;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.BindingMessages;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Customizes a {@link Binding} between two
+ * {@link IObservableList observable lists}. The following behaviors can be
+ * customized via the strategy:
+ * <ul>
+ * <li>Conversion</li>
+ * <li>Automatic processing</li>
+ * </ul>
+ * <p>
+ * Conversion:<br/> When elements are added they can be
+ * {@link #convertcast(Object) converted} to the destination element type.
+ * </p>
+ * <p>
+ * Automatic processing:<br/> 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}).
+ * </p>
+ *
+ *
+ * @see DataBindingContext#bindList(IObservableList, IObservableList,
+ *      UpdateListStrategy, UpdateListStrategy)
+ * @see IConverter
+ * @since 1.0
+ */
+public class UpdateListStrategy : UpdateStrategy {
+
+    /**
+     * Policy constant denoting that the source observable's state should not be
+     * tracked and that the destination observable's state should never be
+     * updated.
+     */
+    public static int POLICY_NEVER = notInlined(1);
+
+    /**
+     * Policy constant denoting that the source observable's state should not be
+     * tracked, but that conversion and updating the destination observable's
+     * state should be performed when explicitly requested.
+     */
+    public static int POLICY_ON_REQUEST = notInlined(2);
+
+    /**
+     * Policy constant denoting that the source observable's state should be
+     * tracked, and that conversion and updating the destination observable's
+     * state should be performed automatically on every change of the source
+     * observable state.
+     */
+    public static int POLICY_UPDATE = notInlined(8);
+
+    /**
+     * Helper method allowing API evolution of the above constant values. The
+     * compiler will not inline constant values into client code if values are
+     * "computed" using this helper.
+     *
+     * @param i
+     *            an integer
+     * @return the same integer
+     */
+    private static int notInlined(int i) {
+        return i;
+    }
+
+    protected IConverter converter;
+
+    private int updatePolicy;
+
+    protected bool provideDefaults;
+
+    /**
+     * Creates a new update list strategy for automatically updating the
+     * destination observable list whenever the source observable list changes.
+     * A default converter will be provided. The defaults can be changed by
+     * calling one of the setter methods.
+     */
+    public this() {
+        this(true, POLICY_UPDATE);
+    }
+
+    /**
+     * Creates a new update list strategy with a configurable update policy. A
+     * default converter will be provided. The defaults can be changed by
+     * calling one of the setter methods.
+     *
+     * @param updatePolicy
+     *            one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, or
+     *            {@link #POLICY_UPDATE}
+     */
+    public this(int updatePolicy) {
+        this(true, updatePolicy);
+    }
+
+    /**
+     * Creates a new update list strategy with a configurable update policy. A
+     * default converter will be provided if <code>provideDefaults</code> is
+     * <code>true</code>. The defaults can be changed by calling one of the
+     * setter methods.
+     *
+     * @param provideDefaults
+     *            if <code>true</code>, 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.
+     * <p>
+     * Default implementation will use the
+     * {@link #setConvertercast(IConverter) converter} if one exists. If no
+     * converter exists no conversion occurs.
+     * </p>
+     *
+     * @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;
+    }
+}
--- /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:
+ * <ul>
+ * <li>Conversion</li>
+ * <li>Automatic processing</li>
+ * </ul>
+ * <p>
+ * Conversion:<br/> When elements are added they can be
+ * {@link #convertcast(Object) converted} to the destination element type.
+ * </p>
+ * <p>
+ * Automatic processing:<br/> 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}).
+ * </p>
+ * 
+ * 
+ * @see DataBindingContext#bindSet(IObservableSet, IObservableSet,
+ *      UpdateSetStrategy, UpdateSetStrategy)
+ * @see IConverter
+ * @since 1.1
+ */
+public class UpdateSetStrategy : UpdateStrategy {
+
+    /**
+     * Policy constant denoting that the source observable's state should not be
+     * tracked and that the destination observable's state should never be
+     * updated.
+     */
+    public final static int POLICY_NEVER = notInlined(1);
+
+    /**
+     * Policy constant denoting that the source observable's state should not be
+     * tracked, but that conversion and updating the destination observable's
+     * state should be performed when explicitly requested.
+     */
+    public final static int POLICY_ON_REQUEST = notInlined(2);
+
+    /**
+     * Policy constant denoting that the source observable's state should be
+     * tracked, and that conversion and updating the destination observable's
+     * state should be performed automatically on every change of the source
+     * observable state.
+     */
+    public final static int POLICY_UPDATE = notInlined(8);
+
+    /**
+     * Helper method allowing API evolution of the above constant values. The
+     * compiler will not inline constant values into client code if values are
+     * "computed" using this helper.
+     * 
+     * @param i
+     *            an integer
+     * @return the same integer
+     */
+    private static int notInlined(int i) {
+        return i;
+    }
+
+    protected IConverter converter;
+
+    private int updatePolicy;
+
+    protected bool provideDefaults;
+
+    /**
+     * Creates a new update list strategy for automatically updating the
+     * destination observable list whenever the source observable list changes.
+     * A default converter will be provided. The defaults can be changed by
+     * calling one of the setter methods.
+     */
+    public this() {
+        this(true, POLICY_UPDATE);
+    }
+
+    /**
+     * Creates a new update list strategy with a configurable update policy. A
+     * default converter will be provided. The defaults can be changed by
+     * calling one of the setter methods.
+     * 
+     * @param updatePolicy
+     *            one of {@link #POLICY_NEVER}, {@link #POLICY_ON_REQUEST}, or
+     *            {@link #POLICY_UPDATE}
+     */
+    public this(int updatePolicy) {
+        this(true, updatePolicy);
+    }
+
+    /**
+     * Creates a new update list strategy with a configurable update policy. A
+     * default converter will be provided if <code>provideDefaults</code> is
+     * <code>true</code>. The defaults can be changed by calling one of the
+     * setter methods.
+     * 
+     * @param provideDefaults
+     *            if <code>true</code>, 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.
+     * <p>
+     * Default implementation will use the
+     * {@link #setConvertercast(IConverter) converter} if one exists. If no
+     * converter exists no conversion occurs.
+     * </p>
+     * 
+     * @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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/UpdateStrategy.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,712 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Matt Carter - Bug 180392
+ *                 - Character support completed (bug 197679)
+ *******************************************************************************/
+
+module org.eclipse.core.databinding.UpdateStrategy;
+
+import java.lang.all;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.conversion.NumberToStringConverter;
+import org.eclipse.core.databinding.conversion.StringToNumberConverter;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.ClassLookupSupport;
+import org.eclipse.core.internal.databinding.Pair;
+import org.eclipse.core.internal.databinding.conversion.CharacterToStringConverter;
+import org.eclipse.core.internal.databinding.conversion.IdentityConverter;
+import org.eclipse.core.internal.databinding.conversion.IntegerToStringConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToBigDecimalConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter;
+import org.eclipse.core.internal.databinding.conversion.ObjectToStringConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToByteConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToShortConverter;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+import com.ibm.icu.text.NumberFormat;
+
+/**
+ * @since 1.0
+ *
+ */
+/* package */class UpdateStrategy {
+
+    private static final String BOOLEAN_TYPE = "java.lang.Boolean.TYPE"; //$NON-NLS-1$
+
+    private static final String SHORT_TYPE = "java.lang.Short.TYPE"; //$NON-NLS-1$
+
+    private static final String BYTE_TYPE = "java.lang.Byte.TYPE"; //$NON-NLS-1$
+
+    private static final String DOUBLE_TYPE = "java.lang.Double.TYPE"; //$NON-NLS-1$
+
+    private static final String FLOAT_TYPE = "java.lang.Float.TYPE"; //$NON-NLS-1$
+
+    private static final String INTEGER_TYPE = "java.lang.Integer.TYPE"; //$NON-NLS-1$
+
+    private static final String LONG_TYPE = "java.lang.Long.TYPE"; //$NON-NLS-1$
+
+    private static final String CHARACTER_TYPE = "java.lang.Character.TYPE"; //$NON-NLS-1$
+
+    private static Map converterMap;
+
+    private static ClassInfo autoboxed(ClassInfo clazz) {
+        if (clazz is Float.TYPE)
+            return Float.classinfo;
+        else if (clazz is Double.TYPE)
+            return Double.classinfo;
+        else if (clazz is Short.TYPE)
+            return Short.classinfo;
+        else if (clazz is Integer.TYPE)
+            return Integer.classinfo;
+        else if (clazz is Long.TYPE)
+            return Long.classinfo;
+        else if (clazz is Byte.TYPE)
+            return Byte.classinfo;
+        else if (clazz is Boolean.TYPE)
+            return Boolean.classinfo;
+        else if (clazz is Character.TYPE)
+            return Character.classinfo;
+        return clazz;
+    }
+
+    final protected void checkAssignable(Object toType, Object fromType,
+            String errorString) {
+        Boolean assignableFromModelToModelConverter = isAssignableFromTo(
+                fromType, toType);
+        if (assignableFromModelToModelConverter !is null
+                && !assignableFromModelToModelConverter.booleanValue()) {
+            throw new BindingException(errorString
+                    + " Expected: " + fromType + ", actual: " + toType); //$NON-NLS-1$//$NON-NLS-2$
+        }
+    }
+
+    /**
+     * Tries to create a converter that can convert from values of type
+     * fromType. Returns <code>null</code> if no converter could be created.
+     * Either toType or modelDescription can be <code>null</code>, but not
+     * both.
+     *
+     * @param fromType
+     * @param toType
+     * @return an IConverter, or <code>null</code> 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 <code>null</code> if
+     * unknown.
+     * 
+     * @param fromType
+     * @param toType
+     * @return whether fromType is assignable to toType, or <code>null</code>
+     *         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;
+        }
+    }
+
+}
--- /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<tom.schindl@bestsolution.at> - bugfix for 217940
+ *******************************************************************************/
+
+module org.eclipse.core.databinding.UpdateValueStrategy;
+
+import java.lang.all;
+
+import java.util.Date;
+import java.util.HashMap;
+
+import org.eclipse.core.databinding.conversion.IConverter;
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.validation.IValidator;
+import org.eclipse.core.databinding.validation.ValidationStatus;
+import org.eclipse.core.internal.databinding.BindingMessages;
+import org.eclipse.core.internal.databinding.Pair;
+import org.eclipse.core.internal.databinding.conversion.NumberToBigDecimalConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToBigIntegerConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToByteConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToDoubleConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToFloatConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToIntegerConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToLongConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToNumberConverter;
+import org.eclipse.core.internal.databinding.conversion.NumberToShortConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToCharacterConverter;
+import org.eclipse.core.internal.databinding.conversion.StringToDateConverter;
+import org.eclipse.core.internal.databinding.validation.NumberFormatConverter;
+import org.eclipse.core.internal.databinding.validation.NumberToByteValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToDoubleValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToFloatValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToIntegerValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToLongValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToShortValidator;
+import org.eclipse.core.internal.databinding.validation.NumberToUnboundedNumberValidator;
+import org.eclipse.core.internal.databinding.validation.ObjectToPrimitiveValidator;
+import org.eclipse.core.internal.databinding.validation.StringToByteValidator;
+import org.eclipse.core.internal.databinding.validation.StringToCharacterValidator;
+import org.eclipse.core.internal.databinding.validation.StringToDateValidator;
+import org.eclipse.core.internal.databinding.validation.StringToDoubleValidator;
+import org.eclipse.core.internal.databinding.validation.StringToFloatValidator;
+import org.eclipse.core.internal.databinding.validation.StringToIntegerValidator;
+import org.eclipse.core.internal.databinding.validation.StringToLongValidator;
+import org.eclipse.core.internal.databinding.validation.StringToShortValidator;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * Customizes a {@link Binding} between two
+ * {@link IObservableValue observable values}. The following behaviors can be
+ * customized via the strategy:
+ * <ul>
+ * <li>Validation</li>
+ * <li>Conversion</li>
+ * <li>Automatic processing</li>
+ * </ul>
+ * <p>
+ * The update phases are:
+ * <ol>
+ * <li>Validate after get - {@link #validateAfterGetcast(Object)}</li>
+ * <li>Conversion - {@link #convertcast(Object)}</li>
+ * <li>Validate after conversion - {@link #validateAfterConvertcast(Object)}</li>
+ * <li>Validate before set - {@link #validateBeforeSetcast(Object)}</li>
+ * <li>Value set - {@link #doSet(IObservableValue, Object)}</li>
+ * </ol>
+ * </p>
+ * <p>
+ * Validation:<br/> {@link IValidator Validators} validate the value at
+ * multiple phases in the update process. Statuses returned from validators are
+ * aggregated into a <code>MultiStatus</code> until a status of
+ * <code>ERROR</code> or <code>CANCEL</code> is encountered. Either of these
+ * statuses will abort the update process. These statuses are available as the
+ * {@link Binding#getValidationStatus() binding validation status}.
+ * </p>
+ * <p>
+ * Conversion:<br/> 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.
+ * </p>
+ * <p>
+ * Automatic processing:<br/> 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}).
+ * </p>
+ *
+ * @see DataBindingContext#bindValue(IObservableValue, IObservableValue,
+ *      UpdateValueStrategy, UpdateValueStrategy)
+ * @see Binding#getValidationStatus()
+ * @see IValidator
+ * @see IConverter
+ * @since 1.0
+ */
+public class UpdateValueStrategy : UpdateStrategy {
+
+    /**
+     * Policy constant denoting that the source observable's state should not be
+     * tracked and that the destination observable's value should never be
+     * updated.
+     */
+    public static int POLICY_NEVER = notInlined(1);
+
+    /**
+     * Policy constant denoting that the source observable's state should not be
+     * tracked, but that validation, conversion and updating the destination
+     * observable's value should be performed when explicitly requested.
+     */
+    public static int POLICY_ON_REQUEST = notInlined(2);
+
+    /**
+     * Policy constant denoting that the source observable's state should be
+     * tracked, including validating changes except for
+     * {@link #validateBeforeSetcast(Object)}, but that the destination
+     * observable's value should only be updated on request.
+     */
+    public static int POLICY_CONVERT = notInlined(4);
+
+    /**
+     * Policy constant denoting that the source observable's state should be
+     * tracked, and that validation, conversion and updating the destination
+     * observable's value should be performed automaticlly on every change of
+     * the source observable value.
+     */
+    public static int POLICY_UPDATE = notInlined(8);
+
+    /**
+     * Helper method allowing API evolution of the above constant values. The
+     * compiler will not inline constant values into client code if values are
+     * "computed" using this helper.
+     *
+     * @param i
+     *            an integer
+     * @return the same integer
+     */
+    private static int notInlined(int i) {
+        return i;
+    }
+
+    protected IValidator afterGetValidator;
+    protected IValidator afterConvertValidator;
+    protected IValidator beforeSetValidator;
+    protected IConverter converter;
+
+    private int updatePolicy;
+
+    private static ValidatorRegistry validatorRegistry = new ValidatorRegistry();
+    private static HashMap validatorsByConverter = new HashMap();
+
+    protected bool provideDefaults;
+
+    /**
+     * <code>true</code> 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
+     * <code>provideDefaults</code> is <code>true</code>. The defaults can
+     * be changed by calling one of the setter methods.
+     *
+     * @param provideDefaults
+     *            if <code>true</code>, 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.
+     * <p>
+     * Default implementation will use the
+     * {@link #setConvertercast(IConverter) converter} if one exists. If no
+     * converter exists no conversion occurs.
+     * </p>
+     *
+     * @param value
+     * @return the converted value
+     */
+    public Object convert(Object value) {
+        return converter is null ? value : converter.convert(value);
+    }
+
+    /**
+     * Tries to create a validator that can validate values of type fromType.
+     * Returns <code>null</code> if no validator could be created. Either
+     * toType or modelDescription can be <code>null</code>, but not both.
+     *
+     * @param fromType
+     * @param toType
+     * @return an IValidator, or <code>null</code> 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 <code>source</code>
+     * and <code>destination</code>. 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 <code>null</code>.
+     *
+     * @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.
+     * <p>
+     * Default implementation will use the
+     * {@link #setAfterConvertValidatorcast(IValidator) validator} if one exists. If
+     * one does not exist no validation will occur.
+     * </p>
+     *
+     * @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.
+     * <p>
+     * Default implementation will use the
+     * {@link #setAfterGetValidatorcast(IValidator) validator} if one exists. If one
+     * does not exist no validation will occur.
+     * </p>
+     *
+     * @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.
+     * <p>
+     * Default implementation will use the
+     * {@link #setBeforeSetValidatorcast(IValidator) validator} if one exists. If
+     * one does not exist no validation will occur.
+     * </p>
+     *
+     * @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;
+                }
+            };
+        }
+    }
+
+}
--- /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;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/ValueBinding.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,248 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Matthew Hall - bug 220700
+ *******************************************************************************/
+
+module org.eclipse.core.databinding.ValueBinding;
+
+import java.lang.all;
+
+import org.eclipse.core.databinding.observable.value.IObservableValue;
+import org.eclipse.core.databinding.observable.value.IValueChangeListener;
+import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
+import org.eclipse.core.databinding.observable.value.WritableValue;
+import org.eclipse.core.databinding.util.Policy;
+import org.eclipse.core.internal.databinding.BindingStatus;
+import org.eclipse.core.internal.databinding.Util;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.Status;
+
+/**
+ * @since 1.0
+ * 
+ */
+class ValueBinding : Binding {
+    private final UpdateValueStrategy targetToModel;
+    private final UpdateValueStrategy modelToTarget;
+    private WritableValue validationStatusObservable;
+    private IObservableValue target;
+    private IObservableValue model;
+
+    private bool updatingTarget;
+    private bool updatingModel;
+    private IValueChangeListener targetChangeListener = new class() IValueChangeListener {
+        public void handleValueChange(ValueChangeEvent event) {
+            if (!updatingTarget && !Util.equals(event.diff.getOldValue(), event.diff.getNewValue())) {
+                doUpdate(target, model, targetToModel, false, false);
+            }
+        }
+    };
+    private IValueChangeListener modelChangeListener = new class() IValueChangeListener {
+        public void handleValueChange(ValueChangeEvent event) {
+            if (!updatingModel && !Util.equals(event.diff.getOldValue(), event.diff.getNewValue())) {
+                doUpdate(model, target, modelToTarget, false, false);
+            }
+        }
+    };
+
+    /**
+     * @param targetObservableValue
+     * @param modelObservableValue
+     * @param targetToModel
+     * @param modelToTarget
+     */
+    public this(IObservableValue targetObservableValue,
+            IObservableValue modelObservableValue,
+            UpdateValueStrategy targetToModel, UpdateValueStrategy modelToTarget) {
+        super(targetObservableValue, modelObservableValue);
+        this.target = targetObservableValue;
+        this.model = modelObservableValue;
+        this.targetToModel = targetToModel;
+        this.modelToTarget = modelToTarget;
+        if ((targetToModel.getUpdatePolicy() & (UpdateValueStrategy.POLICY_CONVERT | UpdateValueStrategy.POLICY_UPDATE)) !is 0) {
+            target.addValueChangeListener(targetChangeListener);
+        } else {
+            targetChangeListener = null;
+        }
+        if ((modelToTarget.getUpdatePolicy() & (UpdateValueStrategy.POLICY_CONVERT | UpdateValueStrategy.POLICY_UPDATE)) !is 0) {
+            model.addValueChangeListener(modelChangeListener);
+        } else {
+            modelChangeListener = null;
+        }
+    }
+
+    protected void preInit() {
+        validationStatusObservable = new WritableValue(context
+                .getValidationRealm(), Status.OK_STATUS, IStatus.classinfo);
+    }
+
+    protected void postInit() {
+        if (modelToTarget.getUpdatePolicy() is UpdateValueStrategy.POLICY_UPDATE) {
+            updateModelToTarget();
+        }
+        if (targetToModel.getUpdatePolicy() !is UpdateValueStrategy.POLICY_NEVER) {
+            validateTargetToModel();
+        }
+    }
+
+    public IObservableValue getValidationStatus() {
+        return validationStatusObservable;
+    }
+
+    public void updateTargetToModel() {
+        doUpdate(target, model, targetToModel, true, false);
+    }
+
+    public void updateModelToTarget() {
+        doUpdate(model, target, modelToTarget, true, false);
+    }
+
+    /**
+     * Incorporates the provided <code>newStats</code> into the
+     * <code>multieStatus</code>.
+     * 
+     * @param multiStatus
+     * @param newStatus
+     * @return <code>true</code> 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();
+    }
+
+}
--- /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;
+    }
+
+}
--- /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);
+}
--- /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 <code>NumberFormat.format(...)</code>.
+ * 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.
+     * <p>
+     * Private to restrict public instantiation.
+     * </p>
+     * 
+     * @param numberFormat
+     * @param fromType
+     */
+    private this(NumberFormat numberFormat, ClassInfo fromType) {
+        super(fromType, String.classinfo);
+
+        this.numberFormat = numberFormat;
+        this.fromType = fromType;
+
+        if (Integer.classinfo.equals(fromType) || Integer.TYPE.equals(fromType)
+                || Long.classinfo.equals(fromType) || Long.TYPE.equals(fromType)) {
+            fromTypeIsLong = true;
+        } else if (Float.classinfo.equals(fromType) || Float.TYPE.equals(fromType)
+                || Double.classinfo.equals(fromType)
+                || Double.TYPE.equals(fromType)) {
+            fromTypeIsDecimalType = true;
+        } else if (BigInteger.classinfo.equals(fromType)) {
+            fromTypeIsBigInteger = true;
+        }
+    }
+
+    /**
+     * Converts the provided <code>fromObject</code> to a <code>String</code>.
+     * If the converter was constructed for an object type, non primitive, a
+     * <code>fromObject</code> of <code>null</code> will be converted to an
+     * empty string.
+     * 
+     * @param fromObject
+     *            value to convert. May be <code>null</code> 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
+     *            <code>true</code> 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
+     *            <code>true</code> 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
+     *            <code>true</code> 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
+     *            <code>true</code> 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);
+    }
+}
--- /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 <code>NumberFormat.parse(...)</code>.
+ * 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 <code>null</code> as
+     * BigInteger doesn't have bounds.
+     */
+    private final Number min;
+    /**
+     * Maximum possible value for the type. Can be <code>null</code> 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 <code>null</code>
+     *            as BigInteger doesn't have bounds
+     * @param max
+     *            maximum possible value for the type, can be <code>null</code>
+     *            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 <code>fromObject</code> 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
+     *            <code>true</code> 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
+     *            <code>true</code> 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
+     *            <code>true</code> 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
+     *            <code>true</code> 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);
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides interfaces and classes for data type conversion.
+<h2>
+Package Specification</h2>
+<p>
+This package provides the <tt>IConverter</tt> interface along with classes
+that implement the interface to convert between common data types.
+<p>
+</body>
+</html>
--- /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$
+    }
+}
--- /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;
+    }
+
+}
--- /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;
+    }
+
+}
--- /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);
+    }
+    
+}
--- /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<size;i++) {
+            Object candidate = list.get(i);
+            if (candidateisnull ? objectisnull : candidate.equals(object)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Checks whether the two objects are <code>null</code> -- allowing for
+     * <code>null</code>.
+     * 
+     * @param left
+     *            The left object to compare; may be <code>null</code>.
+     * @param right
+     *            The right object to compare; may be <code>null</code>.
+     * @return <code>true</code> if the two objects are equivalent;
+     *         <code>false</code> 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_;
+            }
+        };
+    }
+}
--- /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);
+
+}
--- /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.
+ * 
+ * <p>
+ * 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}.
+ * </p>
+ * 
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ *              Clients should instead subclass one of the classes in the
+ *              framework that implement this interface. Note that direct
+ *              implementers of this interface outside of the framework will be
+ *              broken in future releases when methods are added to this
+ *              interface.
+ * 
+ * @since 1.0
+ * 
+ */
+public interface IObservable {
+
+    /**
+     * Returns the realm for this observable. Unless otherwise specified,
+     * getters and setters must be accessed from within this realm. Listeners
+     * will be within this realm when they receive events from this observable.
+     * <p>
+     * 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.
+     * </p>
+     * 
+     * @return the realm
+     */
+    public Realm getRealm();
+
+    /**
+     * Adds the given change listener to the list of change listeners. Change
+     * listeners are notified about changes of the state of this observable in a
+     * generic way, without specifying the change that happened. To get the
+     * changed state, a change listener needs to query for the current state of
+     * this observable.
+     * 
+     * @param listener
+     */
+    public void addChangeListener(IChangeListener listener);
+
+    /**
+     * Removes the given change listener from the list of change listeners. Has
+     * no effect if the given listener is not registered as a change listener.
+     * 
+     * @param listener
+     */
+    public void removeChangeListener(IChangeListener listener);
+
+    /**
+     * Adds the given stale listener to the list of stale listeners. Stale
+     * listeners are notified when an observable object becomes stale, not when
+     * is becomes non-stale.
+     * 
+     * @param listener
+     * 
+     * @see #isStale()
+     */
+    public void addStaleListener(IStaleListener listener);
+
+    /**
+     * Removes the given stale listener from the list of stale listeners. Has no
+     * effect if the given listener is not registered as a stale listener.
+     * 
+     * @param listener
+     */
+    public void removeStaleListener(IStaleListener listener);
+
+    /**
+     * Returns whether the state of this observable is stale and is expected to
+     * change soon. A non-stale observable that becomes stale will notify its
+     * stale listeners. A stale object that becomes non-stale does so by
+     * changing its state and notifying its change listeners, it does <b>not</b>
+     * notify its stale listeners about becoming non-stale. Clients that do not
+     * expect asynchronous changes may ignore staleness of observable objects.
+     * 
+     * @return true if this observable's state is stale and will change soon.
+     * 
+     * @TrackedGetter - implementers must call
+     *                {@link ObservableTracker#getterCalledcast(IObservable)}.
+     */
+    public bool isStale();
+
+    /**
+     * Disposes of this observable object, removing all listeners registered
+     * with this object, and all listeners this object might have registered on
+     * other objects.
+     */
+    public void dispose();
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.core.databinding/src/org/eclipse/core/databinding/observable/IObservableCollection.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,45 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+module org.eclipse.core.databinding.observable.IObservableCollection;
+
+import java.lang.all;
+
+import java.util.Collection;
+
+import org.eclipse.core.databinding.observable.list.IObservableList;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+
+/**
+ * Interface for observable collections. Only general change listeners can be
+ * added to an observable collection. Listeners interested in incremental
+ * changes have to be added using more concrete subtypes such as
+ * {@link IObservableList} or {@link IObservableSet}.
+ * 
+ * @noextend This interface is not intended to be extended by clients.
+ * @noimplement This interface is not intended to be implemented by clients.
+ *              Clients should instead subclass one of the classes that
+ *              implement this interface. Note that direct implementers of this
+ *              interface outside of the framework will be broken in future
+ *              releases when methods are added to this interface.
+ *              </p>
+ * 
+ * @since 1.0
+ */
+public interface IObservableCollection : IObservable, Collection {
+
+    /**
+     * @return the element type of this observable value, or <code>null</code>
+     *         if this observable collection is untyped.
+     */
+    Object getElementType();
+
+}
--- /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 {
+
+}
--- /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 <code>null</code> if this observing
+     * object does not currently observe an object.
+     * 
+     * @return the observed object, or <code>null</code>
+     */
+    public Object getObserved();
+
+}
--- /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);
+
+}
--- /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();
+
+}
--- /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:
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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
+     *            <code>null</code> 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);
+        }
+    }
+}
--- /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 <code>Boolean.TYPE</code> 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.
+     * <p>
+     * The state where the key does not exist in the map is equivalent to the
+     * state where the key exists and its value is <code>null</code>. 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 <code>null</code>, 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 <code>null</code>, 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 <code>MasterDetailObservables.detailValue(master,
+     * mapEntryValueFactory(map, valueType), valueType)</code>.
+     * 
+     * @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 <code>null</code>, 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);
+    }
+}
--- /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.
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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)}.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @since 1.0
+ * 
+ * @see IObservable
+ */
+public abstract class Realm {
+
+    private static ThreadLocal defaultRealm = new ThreadLocal();
+
+    /**
+     * Returns the default realm for the calling thread, or <code>null</code>
+     * if no default realm has been set.
+     * 
+     * @return the default realm, or <code>null</code>
+     */
+    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 <code>null</code>
+     * @return the previous default realm, or <code>null</code>
+     */
+    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
+     * <code>handleException<code> 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 <code>run()</code> 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.
+     * <p>
+     * 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.
+     * </p>
+     * 
+     * @param runnable
+     */
+    public void exec(Runnable runnable) {
+        if (isCurrent()) {
+            safeRun(runnable);
+        } else {
+            asyncExec(runnable);
+        }
+    }
+
+    /**
+     * Causes the <code>run()</code> 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.
+     * <p>
+     * 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.
+     * </p>
+     * <p>
+     * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable.
+     * </p>
+     * 
+     * @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 <code>run()</code> 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.
+     * <p>
+     * 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.
+     * </p>
+     * <p>
+     * Subclasses should use {@link #safeRuncast(Runnable)} to run the runnable.
+     * </p>
+     * <p>
+     * Note: This class is not meant to be called by clients and therefore has
+     * only protected access.
+     * </p>
+     * 
+     * @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 <code>realm</code> 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);
+        }
+    }
+}
--- /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;
+    }
+
+}
--- /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().
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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 <code>oldIndex</code> to
+     * <code>newIndex</code>. This method is equivalent to calling
+     * <code>add(newIndex, remove(oldIndex))</code>.
+     * <p>
+     * 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 <code>0 &lt;= oldIndex &lt; size()</code>.
+     * @param newIndex
+     *            the element's position after the move. Must be within the
+     *            range <code>0 &lt;= newIndex &lt; size()</code>.
+     * @return the element that was moved.
+     * @throws IndexOutOfBoundsException
+     *             if either argument is out of range (<code>0 &lt;= index &lt; size()</code>).
+     * @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$
+    }
+}
--- /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
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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 <code>null</code> 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 <code>null</code> 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.
+     * 
+     * <p>
+     * The Runnable calls calculate and stores the result in cachedList.
+     * </p>
+     * 
+     * <p>
+     * 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.
+     * </p>
+     * 
+     * <p>
+     * The IChangeListener is attached to every dependency.
+     * </p>
+     * 
+     */
+    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();
+    }
+}
--- /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);
+
+}
--- /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 <code>oldIndex</code> to
+     * <code>newIndex</code>. This method is equivalent to calling
+     * <code>add(newIndex, remove(oldIndex))</code>.
+     * <p>
+     * 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 <code>0 &lt;= oldIndex &lt; size()</code>.
+     * @param newIndex
+     *            the element's position after the move. Must be within the
+     *            range <code>0 &lt;= newIndex &lt; size()</code>.
+     * @return the element that was moved.
+     * @throws IndexOutOfBoundsException
+     *             if either argument is out of range (<code>0 &lt;= index &lt; size()</code>).
+     * @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 <code>null</code> if untyped
+     */
+    Object getElementType();
+}
--- /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;
+    }
+
+}
--- /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 <code>visitor</code> for each difference.
+     * <ol>
+     * <li>{@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.
+     * <li>{@link ListDiffVisitor#handleMove(int, int, Object)} is called
+     * whenever a remove entry is immediately followed by an add entry with an
+     * equivalent element.
+     * <li>{@link ListDiffVisitor#handleRemove(int, Object)} is called whenever
+     * a remove entry does not match conditions 1 or 2.
+     * <li>{@link ListDiffVisitor#handleAdd(int, Object)} is called whenever an
+     * add entry does not match conditions in 1 or 2.
+     * </ol>
+     * 
+     * @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();
+    }
+}
--- /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();
+    }
+}
--- /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 <code>element</code> was added to the list at
+     * position <code>index</code>.
+     * 
+     * @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 <code>element</code> was removed from the
+     * list at position <code>index</code>.
+     * 
+     * @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 <code>element</code> was moved in the list
+     * from position <code>oldIndex</code> to position <code>newIndex</code>.
+     * <p>
+     * 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 <code>oldElement</code>, located at position
+     * <code>index</code> in the list, was replaced by <code>newElement</code>.
+     * <p>
+     * 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);
+    }
+}
--- /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. 
+ * <p>
+ * 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.
+ * </p>
+ * @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 <code>oldIndex</code> to
+     * <code>newIndex</code>. This method is equivalent to calling
+     * <code>add(newIndex, remove(oldIndex))</code>.
+     * <p>
+     * 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 <code>0 &lt;= oldIndex &lt; size()</code>.
+     * @param newIndex
+     *            the element's position after the move. Must be within the
+     *            range <code>0 &lt;= newIndex &lt; size()</code>.
+     * @return the element that was moved.
+     * @throws IndexOutOfBoundsException
+     *             if either argument is out of range (<code>0 &lt;= index &lt; size()</code>).
+     * @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);
+    }
+
+}
--- /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.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @since 1.0
+ */
+public class WritableList : ObservableList {
+
+    /**
+     * Creates an empty writable list in the default realm with a
+     * <code>null</code> element type.
+     * 
+     */
+    public this() {
+        this(Realm.getDefault());
+    }
+
+    /**
+     * Creates an empty writable list with a <code>null</code> 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 <code>null</code>
+     */
+    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 <code>null</code>
+     */
+    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 <code>null</code>
+     * @return new list with the default realm.
+     */
+    public static WritableList withElementType(Object elementType) {
+        return new WritableList(Realm.getDefault(), new ArrayList(),
+                elementType);
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes for observing changes in lists.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes for observing changes in lists.</p>
+</body>
+</html>
--- /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;
+
+/**
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * @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$
+    }
+}
--- /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;
+
+/**
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * @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);
+        }
+    }
+
+}
--- /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.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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
+     * <code>firstMap</code>, it cannot be passed in as an argument. Instead,
+     * the second map will be created by calling
+     * <code>secondMapFactory.createObservable(valueSet())</code>.
+     * 
+     * @param firstMap
+     *            the first map
+     * @param secondMapFactory
+     *            a factory that creates the second map when given an observable
+     *            set representing the value set of <code>firstMap</code>.
+     */
+    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;
+    }
+
+}
--- /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);
+}
--- /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);
+
+}
--- /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();
+}
--- /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;
+    }
+
+}
--- /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);
+}
--- /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;
+
+/**
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * @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();
+    }
+}
--- /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;
+
+/**
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * @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 <code>value</code> with the <code>key</code>.  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 <code>key</code>.  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 <code>map</code>'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));
+        }
+    }
+
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes that can be used to observe changes in maps.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to observe changes in maps.</p>
+</body>
+</html>
--- /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);
+
+}
--- /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 <code>null</code>
+     * @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 <code>null</code>
+     * @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 <code>null</code>
+     * @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);
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes that can be used to observe a detail of a master object.
+<h2>
+Package Specification</h2>
+<p>
+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.</p>
+</body>
+</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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides the core APIs for observing changes in objects.
+<h2>
+Package Specification</h2>
+<p>
+This package provides the core APIs for observing changes in objects.</p>
+</body>
+</html>
--- /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}.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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;
+        }
+    }
+    
+}
--- /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 <code>null</code> 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();
+
+}
--- /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);
+
+}
--- /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.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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;
+        }
+    }
+
+}
--- /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;
+
+/**
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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);
+    }
+
+}
--- /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}. 
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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;
+    }
+}
--- /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;
+    }
+
+}
--- /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();
+    }
+}
--- /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.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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();
+    }
+
+}
--- /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}.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @since 1.0
+ */
+public class WritableSet : ObservableSet {
+
+    /**
+     * Constructs a new empty instance in the default realm with a
+     * <code>null</code> 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 <code>null</code>
+     */
+    public this(Collection c, Object elementType) {
+        this(Realm.getDefault(), new HashSet(c), elementType);
+    }
+
+    /**
+     * Constructs a new empty instance in the given realm and a
+     * <code>null</code> 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 <code>null</code>
+     */
+    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 <code>null</code>
+     * @return new instance with the default realm
+     */
+    public static WritableSet withElementType(Object elementType) {
+        return new WritableSet(Realm.getDefault(), new HashSet(), elementType);
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes that can be used to observe changes in sets.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to observe changes in sets.</p>
+</body>
+</html>
--- /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;
+
+/**
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * @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();
+    }
+}
--- /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;
+
+/**
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * @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();
+    }
+
+}
--- /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;
+
+}
--- /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
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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 <code>null</code> if we are not currently listening.
+     */
+    private IObservable[] dependencies = null;
+
+    /**
+     * 
+     */
+    public this() {
+        this(Realm.getDefault(), null);
+    }
+
+    /**
+     * @param valueType
+     *            can be <code>null</code>
+     */
+    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.
+     * 
+     * <p>
+     * The Runnable calls computeValue and stores the result in cachedValue.
+     * </p>
+     * 
+     * <p>
+     * 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.
+     * </p>
+     * 
+     * <p>
+     * The IChangeListener is attached to every dependency.
+     * </p>
+     * 
+     */
+    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();
+    }
+
+}
--- /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 <code>null</code> if this
+     * observable value is untyped.
+     * 
+     * @return the value type, or <code>null</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);
+}
--- /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);
+
+}
--- /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);
+
+}
--- /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);
+
+}
--- /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;
+    }
+
+}
--- /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 <code>true</code>.
+ * 
+ * @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 <code>false</code>, can
+     * be set to <code>true</code> 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;
+    }
+
+}
--- /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();
+    }
+}
--- /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.
+ * <p>
+ * 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.
+ * </p>
+ * @since 1.0
+ */
+public class WritableValue : AbstractObservableValue {
+
+    private final Object valueType;
+
+    /**
+     * Constructs a new instance with the default realm, a <code>null</code>
+     * value type, and a <code>null</code> value.
+     */
+    public this() {
+        this(null, null);
+    }
+
+    /**
+     * Constructs a new instance with the default realm.
+     * 
+     * @param initialValue
+     *            can be <code>null</code>
+     * @param valueType
+     *            can be <code>null</code>
+     */
+    public this(Object initialValue, Object valueType) {
+        this(Realm.getDefault(), initialValue, valueType);
+    }
+
+    /**
+     * Constructs a new instance with the provided <code>realm</code>, a
+     * <code>null</code> value type, and a <code>null</code> initial value.
+     * 
+     * @param realm
+     */
+    public this(Realm realm) {
+        this(realm, null, null);
+    }
+
+    /**
+     * Constructs a new instance.
+     * 
+     * @param realm
+     * @param initialValue
+     *            can be <code>null</code>
+     * @param valueType
+     *            can be <code>null</code>
+     */
+    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 <code>null</code>
+     * @return new instance with the default realm and a value of <code>null</code>
+     */
+    public static WritableValue withValueType(Object elementType) {
+        return new WritableValue(Realm.getDefault(), null, elementType);
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes that can be used to observe changes in discrete values.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to observe changes in discrete values.</p>
+</body>
+</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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes for binding observable objects, for example UI widgets and model objects.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to synchronize state between pairs of
+observable objects with optional data type conversion and validation.
+<p>
+A <tt>DataBindingContext</tt> is used to manage a list of <tt>Bindings</tt>
+with their validation results.
+</p>
+<p>
+Concrete subclasses of <tt>Binding</tt> 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.
+</p>
+<p>
+For each binding, two <tt>UpdateValueStrategy</tt> or <tt>UpdateListStrategy</tt> 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.  
+</p>
+<p>
+<tt>AggregateValidationStatus</tt> allows clients to aggregate the current validation
+statuses of a list of bindings, typically obtained from a data binding context.  
+</p>
+<p>
+For advanced validation, conversion, or similar requirements that affect the way
+state is copied from one side to the other, subclasses of <tt>UpdateValueStrategy</tt>
+or <tt>UpdateListStrategy</tt> can be created.  
+</p>
+</body>
+</html>
--- /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.
+ * <p>
+ * Clients may provide their own implementation to change how errors are logged
+ * from within JFace Data Binding.
+ * </p>
+ * 
+ * @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);
+
+}
--- /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 <tom.schindl@bestsolution.at> - 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 <code>null</code> 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.
+     * <p>
+     * The default logger prints the status to <code>System.err</code>.
+     * </p>
+     * 
+     * @return the logger
+     */
+    public static synchronized ILogger getLog() {
+        if (log is null) {
+            log = getDummyLog();
+        }
+        return log;
+    }
+
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides general utilities for data binding.
+<h2>
+Package Specification</h2>
+<p>
+This package provides general utilities for data binding.</p>
+</body>
+</html>
--- /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);
+    
+}
--- /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.
+ * 
+ * <p>
+ * Some practical examples of cross-constraints:
+ * <ul>
+ * <li>A start date cannot be later than an end date
+ * <li>A list of percentages should add up to 100%
+ * </ul>
+ * <p>
+ * Example: require two integer fields to contain either both even or both odd
+ * numbers.
+ * 
+ * <pre>
+ * 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(&quot;Values must be both even or both odd&quot;);
+ *      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);
+ * </pre>
+ * 
+ * <p>
+ * 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.
+ * 
+ * <pre>
+ * 
+ * ...
+ * 
+ * // 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);
+ * </pre>
+ * 
+ * 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.
+     * <p>
+     * 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.
+     * <p>
+     * The wrapper behaves as follows with respect to the validation status:
+     * <ul>
+     * <li>While valid, the wrapper stays in sync with its target observable.
+     * <li>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.
+     * <li>When status changes from invalid to valid, the wrapper takes the
+     * value of the target observable, and synchronization resumes.
+     * </ul>
+     * 
+     * @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.
+     * <p>
+     * The wrapper behaves as follows with respect to the validation status:
+     * <ul>
+     * <li>While valid, the wrapper stays in sync with its target observable.
+     * <li>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.
+     * <li>When status changes from invalid to valid, the wrapper takes the
+     * elements of the target observable, and synchronization resumes.
+     * </ul>
+     * 
+     * @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.
+     * <p>
+     * The wrapper behaves as follows with respect to the validation status:
+     * <ul>
+     * <li>While valid, the wrapper stays in sync with its target observable.
+     * <li>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.
+     * <li>When status changes from invalid to valid, the wrapper takes the
+     * elements of the target observable, and synchronization resumes.
+     * </ul>
+     * 
+     * @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.
+     * <p>
+     * The wrapper behaves as follows with respect to the validation status:
+     * <ul>
+     * <li>While valid, the wrapper stays in sync with its target observable.
+     * <li>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.
+     * <li>When status changes from invalid to valid, the wrapper takes the
+     * entries of the target observable, and synchronization resumes.
+     * </ul>
+     * 
+     * @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();
+    }
+
+}
--- /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;
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides the core APIs for validation.
+<h2>
+Package Specification</h2>
+<p>
+This package provides the core APIs for validation.</p>
+</body>
+</html>
--- /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;
+        }
+    }
+
+}
--- /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<tom.schindl@bestsolution.at> - 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;
+        }
+    }
+}
--- /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 <code>MultiStatus</code> 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:
+     * <ul>
+     * <li>plugin = Policy.JFACE_DATABINDING</li>
+     * <li>severity = 0</li>
+     * <li>code = 0</li>
+     * <li>message = ""</li>
+     * <li>exception = null</li>
+     * </ul>
+     * 
+     * @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;
+    }
+}
--- /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 <b>sole<b> 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);
+    }
+
+
+}
--- /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);
+    }
+}
--- /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;
+    }
+
+}
--- /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 <code>true</code> if the list is empty.
+     * 
+     * @return <code>true</code> if the list is empty
+     */
+    public bool isEmpty() {
+        return first is null;
+    }
+}
--- /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();
+    }
+
+}
--- /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 <code>null</code> -- allowing for
+     * <code>null</code>.
+     * 
+     * @param left
+     *            The left object to compare; may be <code>null</code>.
+     * @param right
+     *            The right object to compare; may be <code>null</code>.
+     * @return <code>true</code> if the two objects are equivalent;
+     *         <code>false</code> otherwise.
+     */
+    public static final bool equals(Object left, Object right) {
+        return left is null ? right is null : ((right !is null) && left
+                .opEquals(right));
+    }
+}
--- /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);
+    }
+
+}
--- /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);
+    }
+
+}
--- /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<tom.schindl@bestsolution.at> - 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.
+ * <p>
+ * 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.
+ * </p>
+ */
+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 <code>index</code>.
+     * <p>
+     * This is for testing purposes only and should not be a part of the API if
+     * this class was to be exposed.
+     * </p>
+     *
+     * @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];
+    }
+}
--- /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;
+    }   
+}
--- /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;
+    }
+
+}
--- /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.
+ * <p>
+ * This class is a temporary as this ability exists in NumberToStringConverter
+ * except that short and byte are missing.
+ * </p>
+ * 
+ * @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);
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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());
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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());
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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;
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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;
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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;
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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;
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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;
+    }
+}
--- /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.
+ * <p>
+ * This class is thread safe.
+ * </p>
+ * 
+ * @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, <code>null</code> 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;
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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;
+    }
+}
--- /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;
+    }
+
+}
--- /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();
+    }
+}
--- /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;
+    }
+
+}
--- /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<tom.schindl@bestsolution.at> - 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;
+    }
+
+}
--- /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);
+        }
+    }
+}
--- /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);
+    }
+
+}
--- /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;
+    }   
+}
--- /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. <code>null</code> 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. <code>null</code>
+         * 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 <code>true</code> if the provided <code>number</code> is in
+     * the range of a integer.
+     *
+     * @param number
+     * @return <code>true</code> 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 <code>number</code>.
+     *
+     * @param number
+     * @param bitLength number of bits allowed to be in range
+     * @return <code>true</code> 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 <code>true</code> if the provided <code>number</code> is in
+     * the range of a long.
+     *
+     * @param number
+     * @return <code>true</code> if in range
+     * @throws IllegalArgumentException
+     *             if the number type is unsupported
+     */
+    public static bool inLongRange(Number number) {
+        return checkInteger(number, 63);
+    }
+
+    /**
+     * Returns <code>true</code> if the provided <code>number</code> is in
+     * the range of a float.
+     *
+     * @param number
+     * @return <code>true</code> 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 <code>true</code> if the provided <code>number</code> is in
+     * the range of a double.
+     *
+     * @param number
+     * @return <code>true</code> 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 <code>true</code> if the provided <code>number</code> is in
+     * the range of a short.
+     *
+     * @param number
+     * @return <code>true</code> if in range
+     */
+    public static bool inShortRange(Number number) {
+        return checkInteger(number, 15);
+    }
+
+    /**
+     * Returns <code>true</code> if the provided <code>number</code> is in
+     * the range of a byte.
+     *
+     * @param number
+     * @return <code>true</code> if in range
+     */
+    public static bool inByteRange(Number number) {
+        return checkInteger(number, 7);
+    }
+}
--- /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
+     *            <code>true</code> 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);
+    }
+}
--- /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
--- /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
+    }
+}
--- /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;
+    }
+}
--- /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;
+    }
+}
--- /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);
+}
--- /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);
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+
+}
--- /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);
+        }
+    }
+
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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.
+ * <ul>
+ * <li>While status is valid, ValidatedObservableValue stays in sync with its
+ * target.
+ * <li>When status becomes invalid, ValidatedObservableValue will retain the
+ * last valid value of its target.
+ * <li>While status is invalid, changes in the target observable cause
+ * ValidatedObservableValue to fire a stale event, to indicate that changes are
+ * pending.
+ * <li>When status becomes valid, pending value changes are performed (if any)
+ * and synchronization resumes.
+ * </ul>
+ * <p>
+ * Note:
+ * <ul>
+ * <li>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}
+ * <li>Calls to {@link #setValuecast(Object)} on the validated observable changes
+ * the value regardless of the validation status.
+ * <li>This class will not forward {@link ValueChangingEvent} events from a
+ * wrapped {@link IVetoableValue}.
+ * </ul>
+ * 
+ * @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();
+    }
+}
--- /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;
+    }
+}
--- /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();
+    }
+
+}
--- /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;
+    }
+
+}
--- /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;
+    }
+
+}
--- /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);
+        }
+    }
+
+}
--- /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);
+}
--- /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 <code>null</code>
+     */
+    public void setChild(Object child, int index);  
+}
--- /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);
+}
--- /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.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * @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 <code>true</code> 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);
+
+}
--- /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();
+}
--- /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);
+
+}
--- /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);
+}
--- /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 <code>null</code>.
+     * When a request fails, the status indicates why the request failed.
+     * A <code>null</code> status is considered to be successful.
+     * 
+     * @param status request status
+     */
+    public void setStatus(IStatus status);
+    
+    /**
+     * Returns the status of this request, or <code>null</code>.
+     * 
+     * @return request status or <code>null</code>
+     */
+    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);
+}
--- /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();
+    }
+
+}
--- /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
+     * <code>null</code> if the underlying tree is not lazy and never contains
+     * duplicate elements.
+     * 
+     * @return the tree path (possibly empty) of the unchanged parent, or
+     *         <code>null</code>
+     */
+    public abstract TreePath getParentPath();
+
+    /**
+     * @param visitor
+     */
+    public void accept(TreeDiffVisitor visitor) {
+        doAccept(visitor, getParentPath());
+    }
+
+}
--- /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);
+            }
+        }
+    }
+
+}
--- /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 <code>true</code> if the tree diff's children should be
+     *         visited; <code>false</code> if they should be skipped.
+     */
+    public abstract bool visit(TreeDiffNode diff, TreePath currentPath);
+}
--- /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.
+ * <p>
+ * Clients may instantiate this class. Not intended to be subclassed.
+ * </p>
+ * 
+ * @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 <code>null</code> 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);
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+To be written.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to ...</p>
+</body>
+</html>
--- /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 <code>value</code>.  An error status is returned if:
+     * <ul>
+     * <li>The value cannot be parsed.</li>
+     * <li>The value is out of range.</li>
+     * </ul>
+     * 
+     * @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 <code>true</code> if in range
+     */
+    protected abstract bool isInRange(Number number);
+}
--- /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;
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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);
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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);
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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);
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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);
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * @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);
+    }
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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 <code>null</code>
+     * @param max
+     *            can be <code>null</code>
+     */
+    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 <code>true</code> if in range
+     */
+    protected abstract bool inRange(Number number);
+}
--- /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.
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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);
+    }
+}
--- /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.).
+ * <p>
+ * Class is thread safe.
+ * </p>
+ * 
+ * @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;
+    }
+}
--- /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<tom.schindl@bestsolution.at> - 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);
+    }
+}
--- /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<tom.schindl@bestsolution.at> - 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));
+    }
+
+}
--- /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);
+    }
+}
--- /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<tom.schindl@bestsolution.at> - 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;
+    }
+
+}
--- /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<tom.schindl@bestsolution.at> - 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);
+        }
+    }
+}
--- /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);
+    }
+}
--- /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);
+    }
+}
--- /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);
+    }
+}
--- /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);
+    }
+}
--- /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);
+    }
+}
--- 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 @@
      * <code>null</code> 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 <code>false</code> 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
--- 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) {
--- /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();
+
+}
--- /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 {
+
+}
--- /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 <code>observable</code> until <code>delay</code> 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 <code>observable</code> until <code>delay</code>
+     *         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
+     * <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Spinner</li>
+     * <li>org.eclipse.swt.widgets.Button</li>
+     * <li>org.eclipse.swt.widgets.Combo</li>
+     * <li>org.eclipse.swt.custom.CCombo</li>
+     * <li>org.eclipse.swt.widgets.List</li>
+     * <li>org.eclipse.swt.widgets.Scale</li>
+     * </ul>
+     * 
+     * @param control
+     * @return observable value
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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
+     * <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Spinner</li>
+     * <li>org.eclipse.swt.widgets.Scale</li>
+     * </ul>
+     * 
+     * @param control
+     * @return observable value
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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
+     * <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Spinner</li>
+     * <li>org.eclipse.swt.widgets.Scale</li>
+     * </ul>
+     * 
+     * @param control
+     * @return observable value
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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
+     * <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Text</li>
+     * </ul>
+     * 
+     * <li>org.eclipse.swt.widgets.Label</li>
+     * @param control
+     * @param event event type to register for change events
+     * @return observable value
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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
+     * <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Label</li>
+     * <li>org.eclipse.swt.widgets.Link (as of 1.2)</li>
+     * <li>org.eclipse.swt.custom.Label</li>
+     * <li>org.eclipse.swt.widgets.Combo</li>
+     * <li>org.eclipse.swt.custom.CCombo</li>
+     * <li>org.eclipse.swt.widgets.Shell</li>
+     * </ul>
+     * 
+     * @param control
+     * @return observable value
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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
+     * <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Combo</li>
+     * <li>org.eclipse.swt.custom.CCombo</li>
+     * <li>org.eclipse.swt.widgets.List</li>
+     * </ul>
+     * 
+     * @param control
+     * @return observable list
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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 <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Table</li>
+     * <li>org.eclipse.swt.widgets.Combo</li>
+     * <li>org.eclipse.swt.custom.CCombo</li>
+     * <li>org.eclipse.swt.widgets.List</li>
+     * </ul>
+     * 
+     * @param control
+     * @return observable value
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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 <code>control</code>. The supported types are:
+     * <ul>
+     * <li>org.eclipse.swt.widgets.Text</li>
+     * </ul>
+     * 
+     * @param control
+     * @return observable value
+     * @throws IllegalArgumentException
+     *             if <code>control</code> 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;
+        }
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes that can be used to observe changes in SWT widgets.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to observe changes in SWT widgets.</p>
+</body>
+</html>
--- /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();
+}
--- /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 {
+}
--- /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 {
+}
--- /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 {
+}
--- /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();
+    }
+}
--- /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<tom.schindl@bestsolution.at> - 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.
+ * 
+ * <p>
+ * 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();
+    }
+}
--- /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.
+ * 
+ * <p>
+ * 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 <code>null</code>
+     *            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();
+    }
+}
--- /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;
+    }
+
+}
--- /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.
+ * 
+ * <p>
+ * 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();
+    }
+}
--- /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.
+ * 
+ * <p>
+ * 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 <code>null</code>
+     *            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();
+    }
+}
--- /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 <code>null</code> 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 <code>viewer</code> and
+     * <code>dbc</code>.
+     * 
+     * @param viewer
+     *            viewer to edit
+     * @param dbc
+     *            dbc to create <code>Bindings</code>
+     */
+    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 <code>true</code>.
+     * 
+     * @see org.eclipse.jface.viewers.EditingSupport#canEdit(java.lang.Object)
+     */
+    protected bool canEdit(Object element) {
+        return true;
+    }
+
+    /**
+     * Default implementation always returns <code>null</code> 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 <code>target</code> and
+     * <code>model</code>. 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();
+        }
+    }
+}
--- /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 <code>null</code>
+     * 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 <code>null</code> 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 <code>null</code> if
+     * the actual children collection should be consulted. The default
+     * implementation returns null; clients should override.
+     * <p>
+     * Intended as an optimization for when the viewer does not need the actual
+     * children. Clients may be able to implement this more efficiently than
+     * <code>getChildren</code>.
+     * </p>
+     * 
+     * @param element
+     *            the element
+     * @return <code>Boolean.TRUE</code> if the given element has children,
+     *         <code>Boolean.FALSE</code> if it has no children, or
+     *         <code>null</code> if the children collection should be
+     *         consulted.
+     */
+    public Boolean hasChildren(Object element) {
+        return null;
+    }
+
+}
--- /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
+     * <code>java.util.List</code> 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 <code>add</code>, <code>addAll</code>, and
+     * <code>set</code>.
+     * 
+     * @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
+     * <code>java.util.List</code> 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 <code>add</code>, <code>addAll</code>, and
+     * <code>set</code>.
+     * 
+     * @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.
+     * <p>
+     * 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
+     * <code>ICheckable</code>.
+     * 
+     * @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);
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes that can be used to observe the JFace Viewer framework.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that can be used to observe the JFace Viewer framework.</p>
+</body>
+</html>
--- /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;
+    }
+}
--- /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 @@
+<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
+<html>
+<head>
+   <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+   <meta name="Author" content="IBM">
+   <meta name="GENERATOR" content="Mozilla/4.5 [en] (Win98; I) [Netscape]">
+   <title>Package-level Javadoc</title>
+</head>
+<body>
+Provides classes that bridge between data binding and the JFace Wizard framework.
+<h2>
+Package Specification</h2>
+<p>
+This package provides classes that bridge between data binding and the JFace Wizard framework.</p>
+</body>
+</html>
--- /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;
+    }
+
+}
--- /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;
+    }
+}
--- /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 <code>realm</code> and <code>widget</code>.
+     * 
+     * @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;
+    }
+}
--- /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.
+     * 
+     * <p>
+     * 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.
+     * </p>
+     * 
+     * @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.
+     * 
+     * <p>
+     * Subclasses should implement this method to provide any code that changes
+     * the appearance of the widget.
+     * </p>
+     * 
+     * @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();
+    }
+
+}
--- /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.
+ *  
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * <p>
+ * Clients should subclass this when copying information from the model to
+ * a control. Typical usage:
+ * </p>
+ * 
+ * <ul>
+ * <li>Override updateControl. It should do whatever is necessary to display
+ *     the contents of the model in the control.</li>
+ * <li>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.</li>
+ * <li>(optional)Extend dispose() to remove any listeners attached in the constructor</li>
+ * </ul>
+ * 
+ * <p>
+ * Example:
+ * </p>
+ * 
+ * <code>
+ * // 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
+ * </code>
+ * 
+ * @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.
+     *  
+     * <p>
+     * Subclasses should overload this method to provide any code that 
+     * changes the appearance of the widget.
+     * </p>
+     */
+    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);
+        }
+    }
+    
+}
--- /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.
+ * 
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * <p>
+ * Clients should subclass this when copying information from the model to a
+ * menu. Typical usage:
+ * </p>
+ * 
+ * <ul>
+ * <li>Override updateMenu. It should do whatever is necessary to display the
+ * contents of the model in the menu.</li>
+ * <li>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.</li>
+ * <li>(optional)Extend dispose() to remove any listeners attached in the
+ * constructor</li>
+ * </ul>
+ * 
+ * @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.
+     *  
+     * <p>
+     * Subclasses should overload this method to provide any code that 
+     * udates the menu.
+     * </p>
+     */
+    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);
+        }
+    }
+    
+}
--- /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.
+     * 
+     * <p>
+     * This is threadsafe.
+     * </p>
+     * 
+     * @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.
+     * 
+     * <p>
+     * This is threadsafe.
+     * </p>
+     * 
+     * @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.
+            }
+        });
+    }
+
+}
--- /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.
+     * 
+     * <p>
+     * Subclasses should overload this method to provide any code that changes
+     * the appearance of the widget.
+     * </p>
+     * 
+     * @param index
+     * @param item
+     *            the item to update
+     * @param element
+     * @since 1.2
+     */
+    protected abstract void updateItem(int index, TableItem item, Object element);
+
+}
--- /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();
+        }
+    }
+}
--- /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);
+}
--- /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);
+        }
+    }
+}
--- /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).
+     * 
+     * <p></p>
+     * <p>When using elements:</p>
+     * 
+     * <ul>
+     * <li>Cycles are permitted (elements can be their own ancestor)</li>
+     * <li>Addition, removal, and refresh are slightly faster</li>
+     * <li>It is not possible to have more than one content provider per tree</li>
+     * <li>The setRootPath(...) method is ignored</li>
+     * </ul>
+     * 
+     * <p></p>
+     * <p>When using TreePaths:</p>
+     * 
+     * <ul>
+     * <li>Cycles are not permitted (elements cannot be their own parent)</li>
+     * <li>Addition, removal, and refresh are slightly slower</li>
+     * <li>It is possible to use more than one content provider in the same tree</li>
+     * <li>The setRootPath(...) method can be used to direct the output to a particular
+     *     subtree</li>
+     * </ul>
+     * 
+     * @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());
+    }
+}
--- /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);
+    }
+
+}
--- /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));
+        }       
+    }
+}
--- /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);
+    }
+}
--- /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()));         
+        }
+    }
+}
--- /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);
+        }
+
+    }
+}
--- /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;
+    }
+
+}
--- /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);
+    }
+}
--- /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));
+        }
+    }
+}
--- /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);
+        }
+    }
+}
--- /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;
+    }
+}
--- /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
+     * <code>ISWTObservableValue</code> and configured to fire change events
+     * once there have been no value changes in the observable for
+     * <code>delay</code> milliseconds.
+     * 
+     * @param delayMillis
+     * @param observable
+     * @throws IllegalArgumentException
+     *             if <code>updateEventType</code> 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();
+            }
+        });
+    }
+}
--- /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;
+    }
+
+}
--- /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);
+    }
+}
--- /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);
+        }
+    }
+}
--- /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);
+        }
+    }
+}
--- /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);
+
+}
--- /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$
+
+}
--- /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)));
+        }
+    }
+}
--- /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;
+    }
+}
--- /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)));
+        }
+    }
+}
--- /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)));
+        }
+    }
+}
--- /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);
+        }
+    }
+}
--- /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));
+        }
+    }
+}
--- /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.
+ * 
+ * <dl>
+ * <dt>Events:</dt>
+ * <dd> If the update event type (specified on construction) is
+ * <code>SWT.Modify</code> a value change event will be fired on every key
+ * stroke. If the update event type is <code>SWT.FocusOut</code> 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.</dd>
+ * </dl>
+ * 
+ * @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
+     * <code>true</code> 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 <code>text</code> widget
+     * and configured to fire change events to its listeners at the time of the
+     * <code>updateEventType</code>.
+     * 
+     * @param text
+     * @param updateEventType
+     *            SWT event constant as to what SWT event to update the model in
+     *            response to. Appropriate values are: <code>SWT.Modify</code>,
+     *            <code>SWT.FocusOut</code>, <code>SWT.None</code>.
+     * @throws IllegalArgumentException
+     *             if <code>updateEventType</code> 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 <code>null</code>
+     * @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 <code>value</code>.
+     * 
+     * @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();
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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();
+    }
+}
--- /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);
+    }
+}
--- /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 <b>before</b> adding them to the
+     * viewer, and must remove old elements from this set <b>after</b> 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;
+    }
+}
--- /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 /* <IObservableCollection> */collectionFactory;
+
+    private Map /* <Object element, TreeNode node> */elementNodes;
+
+    private TreeStructureAdvisor structureAdvisor;
+
+    /**
+     * Constructs an ObservableCollectionTreeContentProvider using the given
+     * parent provider and collection factory.
+     * 
+     * @param collectionFactory
+     *            observable factory that produces an IObservableList of
+     *            children for a given parent element.
+     * @param structureAdvisor
+     */
+    protected this(
+            IObservableFactory collectionFactory,
+            TreeStructureAdvisor structureAdvisor) {
+        this.structureAdvisor = structureAdvisor;
+        realm = SWTObservables.getRealm(Display.getDefault());
+        viewerObservable = new WritableValue(realm);
+        viewerUpdater = null;
+
+        // Known elements is a detail set of viewerObservable, so that when we
+        // get the viewer instance we can swap in a set that uses its
+        // IElementComparer, if any.
+        IObservableFactory knownElementsFactory = new class() IObservableFactory {
+            public IObservable createObservable(Object target) {
+                return ObservableViewerElementSet.withComparer(realm, null,
+                        getElementComparer(cast(Viewer) target));
+            }
+        };
+        knownElements = MasterDetailObservables.detailSet(viewerObservable,
+                knownElementsFactory, null);
+        unmodifiableKnownElements = Observables
+                .unmodifiableObservableSet(knownElements);
+
+        Assert
+                .isNotNull(collectionFactory,
+                        "Collection factory cannot be null"); //$NON-NLS-1$
+        this.collectionFactory = collectionFactory;
+    }
+
+    public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+        if (elementNodes !is null && !elementNodes.isEmpty()) {
+            // Ensure we flush any observable collection listeners
+            TreeNode[] oldNodes = new TreeNode[elementNodes.size()];
+            elementNodes.values().toArray(oldNodes);
+            for (int i = 0; i < oldNodes.length; i++)
+                oldNodes[i].dispose();
+            elementNodes.clear();
+            elementNodes = null;
+        }
+
+        setViewer(viewer);
+    }
+
+    private void setViewer(Viewer viewer) {
+        viewerUpdater = createViewerUpdater(viewer);
+        comparer = getElementComparer(viewer);
+        elementNodes = ViewerElementMap.withComparer(comparer);
+        viewerObservable.setValue(viewer); // (clears knownElements)
+    }
+
+    private static IElementComparer getElementComparer(Viewer viewer) {
+        if (null !is cast(StructuredViewer)viewer)
+            return (cast(StructuredViewer) viewer).getComparer();
+        return null;
+    }
+
+    private static TreeViewerUpdater createViewerUpdater(Viewer viewer) {
+        if (null !is cast(AbstractTreeViewer)viewer)
+            return new TreeViewerUpdater(cast(AbstractTreeViewer) viewer);
+        throw new IllegalArgumentException(
+                "This content provider only works with AbstractTreeViewer"); //$NON-NLS-1$
+    }
+
+    public Object getParent(Object element) {
+        if (structureAdvisor !is null) {
+            Object parentFromAdvisor = structureAdvisor.getParent(element);
+            if (parentFromAdvisor !is null) {
+                return parentFromAdvisor;
+            }
+        }
+        TreeNode node = getExistingNode(element);
+        if (node !is null)
+            return node.getParent();
+        return null;
+    }
+
+    public Object[] getElements(Object input) {
+        return getChildren(input);
+    }
+
+    public Object[] getChildren(Object element) {
+        Object[] children = getOrCreateNode(element).getChildren();
+        for (int i = 0; i < children.length; i++)
+            getOrCreateNode(children[i]).addParent(element);
+        return children;
+    }
+
+    public bool hasChildren(Object element) {
+        if (structureAdvisor !is null) {
+            Boolean hasChildren = structureAdvisor.hasChildren(element);
+            if (hasChildren !is null) {
+                return hasChildren.booleanValue();
+            }
+        }
+        return getOrCreateNode(element).hasChildren();
+    }
+
+    protected TreeNode getOrCreateNode(Object element) {
+        TreeNode node = getExistingNode(element);
+        if (node is null) {
+            node = new TreeNode(element);
+        }
+        return node;
+    }
+
+    protected TreeNode getExistingNode(Object element) {
+        TreeNode node = cast(TreeNode) elementNodes.get(element);
+        return node;
+    }
+
+    protected bool isViewerDisposed() {
+        Viewer viewer = cast(Viewer) viewerObservable.getValue();
+        return viewer is null || viewer.getControl() is null
+                || viewer.getControl().isDisposed();
+    }
+
+    public void dispose() {
+        if (elementNodes !is null) {
+            if (!elementNodes.isEmpty()) {
+                TreeNode[] nodes = new TreeNode[elementNodes.size()];
+                elementNodes.values().toArray(nodes);
+                for (int i = 0; i < nodes.length; i++) {
+                    nodes[i].dispose();
+                }
+                elementNodes.clear();
+            }
+            elementNodes = null;
+        }
+        if (viewerObservable !is null) {
+            viewerObservable.setValue(null);
+            viewerObservable.dispose();
+            viewerObservable = null;
+        }
+        viewerUpdater = null;
+        comparer = null;
+        knownElements = null;
+        unmodifiableKnownElements = null;
+        collectionFactory = null;
+    }
+
+    /**
+     * Returns the set of elements known to this content provider. Label
+     * providers may track this set if they need to be notified about additions
+     * before the viewer sees the added element, and notified about removals
+     * after the element was removed from the viewer. This is intended for use
+     * by label providers, as it will always return the items that need labels.
+     * 
+     * @return unmodifiable observable set of items that will need labels
+     */
+    public IObservableSet getKnownElements() {
+        return unmodifiableKnownElements;
+    }
+
+    /**
+     * Returns a listener which, when a collection change event is received,
+     * updates the tree viewer through the {@link #viewerUpdater} field, and
+     * maintains the adds and removes parents from the appropriate tree nodes.
+     * 
+     * @param parentElement
+     *            the element that is the parent element of all elements in the
+     *            observable collection.
+     * @return a listener which updates the viewer when change events occur.
+     */
+    protected abstract IObservablesListener createCollectionChangeListener(
+            Object parentElement);
+
+    /**
+     * Registers the change listener to receive change events for the specified
+     * observable collection.
+     * 
+     * @param collection
+     *            the collection to observe for changes
+     * @param listener
+     *            the listener that will receive collection change events.
+     */
+    protected abstract void addCollectionChangeListener(
+            IObservableCollection collection, IObservablesListener listener);
+
+    /**
+     * Unregisters the change listener from receving change events for the
+     * specified observable collection.
+     * 
+     * @param collection
+     *            the collection to stop observing.
+     * @param listener
+     *            the listener to remove
+     */
+    protected abstract void removeCollectionChangeListener(
+            IObservableCollection collection, IObservablesListener listener);
+
+    protected final class TreeNode {
+        private Object element;
+
+        private Object parent;
+        private Set parentSet;
+
+        private IObservableCollection children;
+
+        private IObservablesListener listener;
+
+        this(Object element) {
+            Assert.isNotNull(element, "element cannot be null"); //$NON-NLS-1$
+            this.element = element;
+            knownElements.add(element);
+            elementNodes.put(element, this);
+        }
+
+        Object getElement() {
+            return element;
+        }
+
+        private bool equal(Object left, Object right) {
+            if (comparer is null)
+                return Util.equals(left, right);
+            return comparer.equals(left, right);
+        }
+
+        public void addParent(Object newParent) {
+            if (parent is null) {
+                parent = newParent;
+            } else if (!equal(parent, newParent)) {
+                if (parentSet is null) {
+                    parentSet = ViewerElementSet.withComparer(comparer);
+                    parentSet.add(parent);
+                }
+                parentSet.add(newParent);
+            }
+        }
+
+        public void removeParent(Object oldParent) {
+            if (parentSet !is null)
+                parentSet.remove(oldParent);
+
+            if (equal(parent, oldParent)) {
+                if (parentSet is null || parentSet.isEmpty()) {
+                    parent = null;
+                } else {
+                    Iterator iterator = parentSet.iterator();
+                    parent = iterator.next();
+                    iterator.remove();
+                }
+            }
+
+            if (parentSet !is null && parentSet.isEmpty())
+                parentSet = null;
+
+            if (parent is null) {
+                dispose();
+            }
+        }
+
+        private Object getParent() {
+            return parent;
+        }
+
+        private void initChildren() {
+            if (children is null) {
+                children = cast(IObservableCollection) collectionFactory
+                        .createObservable(element);
+                if (children is null) {
+                    listener = null;
+                    children = Observables.emptyObservableSet(realm);
+                } else {
+                    Assert
+                            .isTrue(Util.equals(realm, children.getRealm()),
+                                    "Children observable collection must be on the Display realm"); //$NON-NLS-1$
+                    listener = createCollectionChangeListener(element);
+                    addCollectionChangeListener(children, listener);
+                }
+            }
+        }
+
+        bool hasChildren() {
+            initChildren();
+            return !children.isEmpty();
+        }
+
+        Object[] getChildren() {
+            initChildren();
+            return children.toArray();
+        }
+
+        private void dispose() {
+            if (element !is null) {
+                elementNodes.remove(element);
+                knownElements.remove(element);
+            }
+            if (children !is null) {
+                for (Iterator iterator = children.iterator(); iterator
+                        .hasNext();) {
+                    TreeNode child = getExistingNode(iterator.next());
+                    if (child !is null)
+                        child.removeParent(element);
+                }
+                if (listener !is null)
+                    removeCollectionChangeListener(children, listener);
+                children.dispose();
+                children = null;
+            }
+            element = null;
+            parent = null;
+            if (parentSet !is null) {
+                parentSet.clear();
+                parentSet = null;
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ObservableViewerElementSet.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,203 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 215531)
+ *     Matthew Hall - bug 230267
+ ******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ObservableViewerElementSet;
+
+import java.lang.all;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.set.AbstractObservableSet;
+import org.eclipse.core.databinding.observable.set.IObservableSet;
+import org.eclipse.core.databinding.observable.set.WritableSet;
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * An {@link IObservableSet} of elements in a {@link StructuredViewer}.
+ * Elements of the set are compared using an {@link IElementComparer} instead of
+ * {@link #equals(Object)}.
+ * <p>
+ * This class is <i>not</i> a strict implementation the {@link IObservableSet}
+ * interface. It intentionally violates the {@link Set} contract, which requires
+ * the use of {@link #equals(Object)} when comparing elements. This class is
+ * designed for use with {@link StructuredViewer} which uses
+ * {@link IElementComparer} for element comparisons.
+ * 
+ * 
+ * @since 1.2
+ */
+public class ObservableViewerElementSet : AbstractObservableSet {
+    private Set wrappedSet;
+    private Object elementType;
+    private IElementComparer comparer;
+
+    /**
+     * Constructs an ObservableViewerElementSet on the given {@link Realm} which
+     * uses the given {@link IElementComparer} to compare elements.
+     * 
+     * @param realm
+     *            the realm of the constructed set.
+     * @param elementType
+     *            the element type of the constructed set.
+     * @param comparer
+     *            the {@link IElementComparer} used to compare elements.
+     */
+    public this(Realm realm, Object elementType,
+            IElementComparer comparer) {
+        super(realm);
+
+        Assert.isNotNull(comparer);
+        this.wrappedSet = new ViewerElementSet(comparer);
+        this.elementType = elementType;
+        this.comparer = comparer;
+    }
+
+    protected Set getWrappedSet() {
+        return wrappedSet;
+    }
+
+    public Object getElementType() {
+        return elementType;
+    }
+
+    public Iterator iterator() {
+        getterCalled();
+        final Iterator wrappedIterator = wrappedSet.iterator();
+        return new class() Iterator {
+            Object last;
+
+            public bool hasNext() {
+                getterCalled();
+                return wrappedIterator.hasNext();
+            }
+
+            public Object next() {
+                getterCalled();
+                return last = wrappedIterator.next();
+            }
+
+            public void remove() {
+                getterCalled();
+                wrappedIterator.remove();
+                fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET,
+                        Collections.singleton(last)));
+            }
+        };
+    }
+
+    public bool add(Object o) {
+        getterCalled();
+        bool changed = wrappedSet.add(o);
+        if (changed)
+            fireSetChange(Diffs.createSetDiff(Collections.singleton(o),
+                    Collections.EMPTY_SET));
+        return changed;
+    }
+
+    public bool addAll(Collection c) {
+        getterCalled();
+        Set additions = new ViewerElementSet(comparer);
+        for (Iterator iterator = c.iterator(); iterator.hasNext();) {
+            Object element = iterator.next();
+            if (wrappedSet.add(element))
+                additions.add(element);
+        }
+        bool changed = !additions.isEmpty();
+        if (changed)
+            fireSetChange(Diffs.createSetDiff(additions, Collections.EMPTY_SET));
+        return changed;
+    }
+
+    public bool remove(Object o) {
+        getterCalled();
+        bool changed = wrappedSet.remove(o);
+        if (changed)
+            fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET,
+                    Collections.singleton(o)));
+        return changed;
+    }
+
+    public bool removeAll(Collection c) {
+        getterCalled();
+        Set removals = new ViewerElementSet(comparer);
+        for (Iterator iterator = c.iterator(); iterator.hasNext();) {
+            Object element = iterator.next();
+            if (wrappedSet.remove(element))
+                removals.add(element);
+        }
+        bool changed = !removals.isEmpty();
+        if (changed)
+            fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+        return changed;
+    }
+
+    public bool retainAll(Collection c) {
+        getterCalled();
+        Set removals = new ViewerElementSet(comparer);
+        Object[] toRetain = c.toArray();
+        outer: for (Iterator iterator = wrappedSet.iterator(); iterator
+                .hasNext();) {
+            Object element = iterator.next();
+            // Cannot rely on c.contains(element) because we must compare
+            // elements using IElementComparer.
+            for (int i = 0; i < toRetain.length; i++) {
+                if (comparer.equals(element, toRetain[i]))
+                    continue outer;
+            }
+            iterator.remove();
+            removals.add(element);
+        }
+        bool changed = !removals.isEmpty();
+        if (changed)
+            fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+        return changed;
+    }
+
+    public void clear() {
+        getterCalled();
+        if (!wrappedSet.isEmpty()) {
+            Set removals = wrappedSet;
+            wrappedSet = new ViewerElementSet(comparer);
+            fireSetChange(Diffs.createSetDiff(Collections.EMPTY_SET, removals));
+        }
+    }
+
+    /**
+     * Returns an {@link IObservableSet} for holding viewer elements, using the
+     * given {@link IElementComparer} for comparisons.
+     * 
+     * @param realm
+     *            the realm of the returned observable
+     * @param elementType
+     *            the element type of the returned set
+     * @param comparer
+     *            the element comparer to use in element comparisons (may be
+     *            null). If null, the returned set will compare elements
+     *            according to the standard contract for {@link Set} interface
+     *            contract.
+     * @return a Set for holding viewer elements, using the given
+     *         {@link IElementComparer} for comparisons.
+     */
+    public static IObservableSet withComparer(Realm realm, Object elementType,
+            IElementComparer comparer) {
+        if (comparer is null)
+            return new WritableSet(realm, Collections.EMPTY_SET, elementType);
+        return new ObservableViewerElementSet(realm, elementType, comparer);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderMultipleSelectionObservableList.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,113 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Peter Centgraf and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Peter Centgraf - initial API and implementation, bug 124683
+ *     Boris Bokowski, IBM Corporation - initial API and implementation
+ *******************************************************************************/
+module org.eclipse.jface.internal.databinding.viewers.SelectionProviderMultipleSelectionObservableList;
+
+import java.lang.all;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.list.ListDiff;
+import org.eclipse.core.databinding.observable.list.WritableList;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+/**
+ * Observes multiple-selection of an {@link ISelectionProvider}.
+ * 
+ * @since 1.2
+ */
+public class SelectionProviderMultipleSelectionObservableList :
+        WritableList {
+
+    protected ISelectionProvider selectionProvider;
+    protected bool handlingSelection;
+    protected bool updating;
+    protected SelectionListener selectionListener = new SelectionListener();
+
+    class SelectionListener : ISelectionChangedListener {
+        public void selectionChanged(SelectionChangedEvent event) {
+            if (updating) {
+                return;
+            }
+            handlingSelection = true;
+            try {
+                updateWrappedList(new ArrayList(getSelectionList(event.getSelection())));
+            } finally {
+                handlingSelection = false;
+            }
+        }
+    }
+
+    /**
+     * Create a new observable list based on the current selection of the given
+     * selection provider. Assumes that the selection provider provides
+     * structured selections.
+     * 
+     * @param realm
+     * @param selectionProvider
+     * @param elementType
+     */
+    public this(Realm realm,
+            ISelectionProvider selectionProvider, Object elementType) {
+        super(realm, new ArrayList(getSelectionList(selectionProvider)), elementType);
+        this.selectionProvider = selectionProvider;
+        selectionProvider.addSelectionChangedListener(selectionListener);
+    }
+
+    protected void fireListChange(ListDiff diff) {
+        if (handlingSelection) {
+            super.fireListChange(diff);
+        } else {
+            // this is a bit of a hack - we are changing the diff to match the order
+            // of elements returned by the selection provider after we've set the
+            // selection.
+            updating = true;
+            try {
+                List oldList = getSelectionList(selectionProvider);
+                selectionProvider
+                        .setSelection(new StructuredSelection(wrappedList));
+                wrappedList = new ArrayList(getSelectionList(selectionProvider));
+                super.fireListChange(Diffs.computeListDiff(oldList, wrappedList));
+            } finally {
+                updating = false;
+            }
+        }
+    }
+
+    protected static List getSelectionList(ISelectionProvider selectionProvider) {
+        if (selectionProvider is null) {
+            throw new IllegalArgumentException();
+        }
+        return getSelectionList(selectionProvider.getSelection());
+    }
+
+    protected static List getSelectionList(ISelection sel) {
+        if (null !is cast(IStructuredSelection)sel) {
+            return (cast(IStructuredSelection) sel).toList();
+        }
+        return Collections.EMPTY_LIST;
+    }
+
+    public synchronized void dispose() {
+        selectionProvider.removeSelectionChangedListener(selectionListener);
+        selectionProvider = null;
+        super.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/SelectionProviderSingleSelectionObservableValue.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Brad Reynolds - bug 137877
+ *     Brad Reynolds - bug 164653
+ *     Brad Reynolds - bug 147515
+ *     Ashley Cambrell - bug 198906
+ *******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.SelectionProviderSingleSelectionObservableValue;
+
+import java.lang.all;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+import org.eclipse.jface.util.Util;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+
+/**
+ * Observes single selection of an <code>ISelectionProvider</code>.
+ *
+ * @since 1.1
+ */
+public class SelectionProviderSingleSelectionObservableValue :
+        AbstractObservableValue {
+
+    private final ISelectionProvider selectionProvider;
+
+    private bool updating = false;
+
+    private Object currentSelection;
+
+    private ISelectionChangedListener selectionChangedListener;
+
+    /**
+     * Constructs a new instance associated with the provided
+     * <code>selectionProvider</code>. In order to initialize itself properly
+     * the constructor invokes {@link #doGetValue()}. This could be dangerous
+     * for subclasses, see {@link #doGetValue()} for an explanation.
+     *
+     * @param realm
+     *
+     * @param selectionProvider
+     * @see #doGetValue()
+     */
+    public this(Realm realm,
+            ISelectionProvider selectionProvider) {
+        super(realm);
+        if (selectionProvider is null) {
+            throw new IllegalArgumentException(
+                    "The 'selectionProvider' parameter is null."); //$NON-NLS-1$
+        }
+
+        this.selectionProvider = selectionProvider;
+        this.currentSelection = doGetValue();
+
+        selectionChangedListener = new class() ISelectionChangedListener {
+            public void selectionChanged(SelectionChangedEvent event) {
+                if (!updating) {
+                    Object oldSelection = currentSelection;
+                    currentSelection = doGetValue();
+                    fireValueChange(Diffs.createValueDiff(oldSelection,
+                            currentSelection));
+                }
+            }
+        };
+        selectionProvider.addSelectionChangedListener(selectionChangedListener);
+    }
+
+    /**
+     * Sets the selection to the provided <code>value</code>. Value change
+     * events are fired after selection is set in the selection provider.
+     *
+     * @param value
+     *            object to set as selected, <code>null</code> if wanting to
+     *            remove selection
+     */
+    public void doSetValue(Object value) {
+        try {
+            updating = true;
+
+            Object oldSelection = currentSelection;
+            selectionProvider
+                    .setSelection(value is null ? StructuredSelection.EMPTY
+                            : new StructuredSelection(value));
+            currentSelection = doGetValue();
+            if (!Util.equals(oldSelection, currentSelection)) {
+                fireValueChange(Diffs.createValueDiff(oldSelection,
+                        currentSelection));
+            }
+        } finally {
+            updating = false;
+        }
+    }
+
+    /**
+     * Retrieves the current selection.
+     * <p>
+     * If a subclass overrides this method it must not depend upon the subclass
+     * to have been fully initialized before this method is invoked.
+     * <code>doGetValue()</code> is invoked by the
+     * {@link #SelectionProviderSingleSelectionObservableValue(Realm, ISelectionProvider) constructor}
+     * which means the subclass's constructor will not have fully executed
+     * before this method is invoked.
+     * </p>
+     *
+     * @return selection will be an instance of
+     *         <code>IStructuredSelection</code> if a selection exists,
+     *         <code>null</code> if no selection
+     * @see #SelectionProviderSingleSelectionObservableValue(Realm,
+     *      ISelectionProvider)
+     */
+    protected Object doGetValue() {
+        ISelection selection = selectionProvider.getSelection();
+        if (null !is cast(IStructuredSelection)selection) {
+            IStructuredSelection sel = cast(IStructuredSelection) selection;
+            return sel.getFirstElement();
+        }
+
+        return null;
+    }
+
+    public Object getValueType() {
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.eclipse.core.databinding.observable.value.AbstractObservableValue#dispose()
+     */
+    public synchronized void dispose() {
+        selectionProvider
+                .removeSelectionChangedListener(selectionChangedListener);
+        super.dispose();
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TableViewerUpdater.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 215531)
+ *     Matthew Hall - bug 226765
+ ******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.TableViewerUpdater;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.AbstractTableViewer;
+
+/**
+ * NON-API - A {@link ViewerUpdater} that updates {@link AbstractTableViewer}
+ * instances.
+ * 
+ * @since 1.2
+ */
+class TableViewerUpdater : ViewerUpdater {
+    private AbstractTableViewer viewer;
+
+    this(AbstractTableViewer viewer) {
+        super(viewer);
+        this.viewer = viewer;
+    }
+
+    public void insert(Object element, int position) {
+        viewer.insert(element, position);
+    }
+
+    public void remove(Object element, int position) {
+        viewer.remove(element);
+    }
+
+    public void replace(Object oldElement, Object newElement, int position) {
+        if (viewer.getComparator() is null && viewer.getFilters().length is 0)
+            viewer.replace(newElement, position);
+        else {
+            super.replace(oldElement, newElement, position);
+        }
+    }
+
+    public void add(Object[] elements) {
+        viewer.add(elements);
+    }
+
+    public void remove(Object[] elements) {
+        viewer.remove(elements);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/TreeViewerUpdater.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,178 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 207858)
+ *     Matthew Hall - bug 226765
+ ******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.TreeViewerUpdater;
+
+import java.lang.all;
+
+import org.eclipse.jface.util.Util;
+import org.eclipse.jface.viewers.AbstractTreeViewer;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeViewer;
+
+/**
+ * NON-API - An interface for sending updates to an {@link AbstractTreeViewer}.
+ * 
+ * @since 1.2
+ */
+public class TreeViewerUpdater {
+    private final AbstractTreeViewer viewer;
+    private final TreeViewer treeViewer;
+
+    /**
+     * Constructs an ITreeViewerUpdater for updating the given viewer.
+     * 
+     * @param viewer
+     *            the viewer that will be updated
+     */
+    public this(AbstractTreeViewer viewer) {
+        this.viewer = viewer;
+        if (null !is cast(TreeViewer)viewer)
+            treeViewer = cast(TreeViewer) viewer;
+        else
+            treeViewer = null;
+    }
+
+    /**
+     * Insert the element into the viewer as a child of the specified parent
+     * element, at the specified position.
+     * 
+     * @param parent
+     *            the parent of the element being inserted
+     * @param element
+     *            the element to insert
+     * @param position
+     *            the position where the element is inserted
+     */
+    public void insert(Object parent, Object element, int position) {
+        viewer.insert(parent, element, position);
+    }
+
+    /**
+     * Replaces the specified element whenever it appears as a child of the
+     * specified parent element, at the given position with the new element.
+     * 
+     * @param parent
+     *            the parent of the element being replaced
+     * @param oldElement
+     *            the element being replaced
+     * @param newElement
+     *            the element that replaces <code>oldElement</code>
+     * @param position
+     *            the position of the element being replaced.
+     */
+    public void replace(Object parent, Object oldElement, Object newElement,
+            int position) {
+        if (treeViewer !is null && viewer.getComparator() is null
+                && viewer.getFilters().length is 0) {
+            treeViewer.replace(parent, position, newElement);
+        } else {
+            remove(parent, oldElement, position);
+            insert(parent, newElement, position);
+        }
+    }
+
+    /**
+     * Moves the specified element from the specified old position to the
+     * specified new position, whenever it appears as a child of the specified
+     * parent element. No action is taken if the viewer has a sorter or
+     * filter(s).
+     * 
+     * @param parent
+     *            the parent of the element being moved
+     * @param element
+     *            the element being moved
+     * @param oldPosition
+     *            the position of the element before it is moved
+     * @param newPosition
+     *            the position of the element after it is moved
+     */
+    public void move(Object parent, Object element, int oldPosition,
+            int newPosition) {
+        if (viewer.getComparator() is null && viewer.getFilters().length is 0) {
+
+            ITreeSelection selection = cast(ITreeSelection) viewer.getSelection();
+
+            remove(parent, element, oldPosition);
+            insert(parent, element, newPosition);
+
+            // Preserve selection
+            if (!selection.isEmpty()) {
+                // If the moved element is selected (or an ancestor of a
+                // selected element), restore the selection.
+                IElementComparer comparer = viewer.getComparer();
+                TreePath[] paths = selection.getPaths();
+                outer: for (int i = 0; i < paths.length; i++) {
+                    TreePath path = paths[i];
+                    for (int j = 0; j < path.getSegmentCount(); j++) {
+                        Object pathElement = path.getSegment(j);
+                        if (comparer is null ? Util
+                                .equals(element, pathElement) : comparer
+                                .equals(element, pathElement)) {
+                            viewer.setSelection(selection);
+                            break outer;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes the element from the from whenever it appears as a child of the
+     * specified parent element, at the specified position.
+     * 
+     * @param parent
+     *            the parent of the element being removed
+     * @param element
+     *            the element to remove
+     * @param position
+     *            the position where the element is located
+     */
+    public void remove(Object parent, Object element, int position) {
+        if (treeViewer !is null && viewer.getComparator() is null
+                && viewer.getFilters().length is 0) {
+            // Only TreeViewer has a remove-by-index method.  
+            treeViewer.remove(parent, position);
+        } else {
+            viewer.remove(parent, [ element ]);
+        }
+    }
+
+    /**
+     * Add the elements into the viewer as children of the specified parent
+     * element.
+     * 
+     * @param parent
+     *            the parent of the element being inserted
+     * @param elements
+     *            the elements to insert
+     */
+    public void add(Object parent, Object[] elements) {
+        viewer.add(parent, elements);
+    }
+
+    /**
+     * Remove the elements from the viewer wherever they appear as children of
+     * the specified parent element.
+     * 
+     * @param parent
+     *            the parent of the elements being removed
+     * @param elements
+     *            the elements to remove
+     */
+    public void remove(Object parent, Object[] elements) {
+        viewer.remove(parent, elements);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementMap.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,434 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 215531)
+ *     Matthew Hall - bug 228125
+ ******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ViewerElementMap;
+
+import java.lang.all;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.util.Util;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * A {@link Map} whose keys are elements in a {@link StructuredViewer}. The
+ * keys in the map are compared using an {@link IElementComparer} instead of
+ * {@link #equals(Object)}.
+ * <p>
+ * This class is <i>not</i> a strict implementation the {@link Map} interface.
+ * It intentionally violates the {@link Map} contract, which requires the use of
+ * {@link #equals(Object)} when comparing keys. This class is designed for use
+ * with {@link StructuredViewer} which uses {@link IElementComparer} for element
+ * comparisons.
+ * 
+ * @since 1.2
+ */
+public class ViewerElementMap : Map { 
+    private Map wrappedMap;
+    private IElementComparer comparer;
+
+    /**
+     * Constructs a ViewerElementMap using the given {@link IElementComparer}.
+     * 
+     * @param comparer
+     *            the {@link IElementComparer} used for comparing keys.
+     */
+    public this(IElementComparer comparer) {
+        Assert.isNotNull(comparer);
+        this.wrappedMap = new HashMap();
+        this.comparer = comparer;
+    }
+
+    /**
+     * Constructs a ViewerElementMap containing all the entries in the specified
+     * map.
+     * 
+     * @param map
+     *            the map whose entries are to be added to this map.
+     * @param comparer
+     *            the {@link IElementComparer} used for comparing keys.
+     */
+    public this(Map map, IElementComparer comparer) {
+        this(comparer);
+        Assert.isNotNull(map);
+        putAll(map);
+    }
+
+    public void clear() {
+        wrappedMap.clear();
+    }
+
+    public bool containsKey(Object key) {
+        return wrappedMap.containsKey(new ViewerElementWrapper(key, comparer));
+    }
+
+    public bool containsValue(Object value) {
+        return wrappedMap.containsValue(value);
+    }
+
+    public Set entrySet() {
+        final Set wrappedEntrySet = wrappedMap.entrySet();
+        return new class() Set {
+            public bool add(Object o) {
+                throw new UnsupportedOperationException();
+            }
+
+            public bool addAll(Collection c) {
+                throw new UnsupportedOperationException();
+            }
+
+            public void clear() {
+                wrappedEntrySet.clear();
+            }
+
+            public bool contains(Object o) {
+                for (Iterator iterator = iterator(); iterator.hasNext();)
+                    if (iterator.next().equals(o))
+                        return true;
+                return false;
+            }
+
+            public bool containsAll(Collection c) {
+                for (Iterator iterator = c.iterator(); iterator.hasNext();)
+                    if (!contains(iterator.next()))
+                        return false;
+                return true;
+            }
+
+            public bool isEmpty() {
+                return wrappedEntrySet.isEmpty();
+            }
+
+            public Iterator iterator() {
+                final Iterator wrappedIterator = wrappedEntrySet.iterator();
+                return new class() Iterator {
+                    public bool hasNext() {
+                        return wrappedIterator.hasNext();
+                    }
+
+                    public Object next() {
+                        Map.Entry wrappedEntry = cast(Map.Entry) wrappedIterator
+                                .next();
+                        return new class() Map.Entry {
+                            public Object getKey() {
+                                return (cast(ViewerElementWrapper) wrappedEntry.getKey())
+                                        .unwrap();
+                            }
+
+                            public Object getValue() {
+                                return wrappedEntry.getValue();
+                            }
+
+                            public Object setValue(Object value) {
+                                return wrappedEntry.setValue(value);
+                            }
+
+                            public bool equals(Object obj) {
+                                if (obj is this)
+                                    return true;
+                                if (obj is null || !(null !is cast(Map.Entry)obj))
+                                    return false;
+                                Map.Entry that = cast(Map.Entry) obj;
+                                return comparer.equals(this.getKey(), that
+                                        .getKey())
+                                        && Util.equals(this.getValue(), that
+                                                .getValue());
+                            }
+
+                            public int hashCode() {
+                                return wrappedEntry.hashCode();
+                            }
+                        };
+                    }
+
+                    public void remove() {
+                        wrappedIterator.remove();
+                    }
+                };
+            }
+
+            public bool remove(Object o) {
+                Map.Entry unwrappedEntry = cast(Map.Entry) o;
+                final ViewerElementWrapper wrappedKey = new ViewerElementWrapper(
+                        unwrappedEntry.getKey(), comparer);
+                Map.Entry wrappedEntry = new class() Map.Entry {
+                    public Object getKey() {
+                        return wrappedKey;
+                    }
+
+                    public Object getValue() {
+                        return unwrappedEntry.getValue();
+                    }
+
+                    public Object setValue(Object value) {
+                        throw new UnsupportedOperationException();
+                    }
+
+                    public bool equals(Object obj) {
+                        if (obj is this)
+                            return true;
+                        if (obj is null || !(null !is cast(Map.Entry)obj))
+                            return false;
+                        Map.Entry that = cast(Map.Entry) obj;
+                        return Util.equals(wrappedKey, that.getKey())
+                                && Util
+                                        .equals(this.getValue(), that
+                                                .getValue());
+                    }
+
+                    public int hashCode() {
+                        return wrappedKey.hashCode()
+                                ^ (getValue() is null ? 0 : getValue()
+                                        .hashCode());
+                    }
+                };
+                return wrappedEntrySet.remove(wrappedEntry);
+            }
+
+            public bool removeAll(Collection c) {
+                bool changed = false;
+                for (Iterator iterator = c.iterator(); iterator.hasNext();)
+                    changed |= remove(iterator.next());
+                return changed;
+            }
+
+            public bool retainAll(Collection c) {
+                bool changed = false;
+                Object[] toRetain = c.toArray();
+                outer: for (Iterator iterator = iterator(); iterator.hasNext();) {
+                    Object entry = iterator.next();
+                    for (int i = 0; i < toRetain.length; i++)
+                        if (entry.equals(toRetain[i]))
+                            continue outer;
+                    iterator.remove();
+                    changed = true;
+                }
+                return changed;
+            }
+
+            public int size() {
+                return wrappedEntrySet.size();
+            }
+
+            public Object[] toArray() {
+                return toArray(new Object[size()]);
+            }
+
+            public Object[] toArray(Object[] a) {
+                int size = size();
+                if (a.length < size) {
+                    a = cast(Object[]) Array.newInstance(a.getClass()
+                            .getComponentType(), size);
+                }
+                int i = 0;
+                for (Iterator iterator = iterator(); iterator.hasNext();) {
+                    a[i] = iterator.next();
+                    i++;
+                }
+                return a;
+            }
+
+            public bool equals(Object obj) {
+                if (obj is this)
+                    return true;
+                if (obj is null || !(null !is cast(Set)obj))
+                    return false;
+                Set that = cast(Set) obj;
+                return this.size() is that.size() && containsAll(that);
+            }
+
+            public int hashCode() {
+                return wrappedEntrySet.hashCode();
+            }
+        };
+    }
+
+    public Object get(Object key) {
+        return wrappedMap.get(new ViewerElementWrapper(key, comparer));
+    }
+
+    public bool isEmpty() {
+        return wrappedMap.isEmpty();
+    }
+
+    public Set keySet() {
+        final Set wrappedKeySet = wrappedMap.keySet();
+        return new class() Set {
+            public bool add(Object o) {
+                throw new UnsupportedOperationException();
+            }
+
+            public bool addAll(Collection c) {
+                throw new UnsupportedOperationException();
+            }
+
+            public void clear() {
+                wrappedKeySet.clear();
+            }
+
+            public bool contains(Object o) {
+                return wrappedKeySet.contains(new ViewerElementWrapper(o, comparer));
+            }
+
+            public bool containsAll(Collection c) {
+                for (Iterator iterator = c.iterator(); iterator.hasNext();)
+                    if (!wrappedKeySet.contains(new ViewerElementWrapper(iterator.next(), comparer)))
+                        return false;
+                return true;
+            }
+
+            public bool isEmpty() {
+                return wrappedKeySet.isEmpty();
+            }
+
+            public Iterator iterator() {
+                final Iterator wrappedIterator = wrappedKeySet.iterator();
+                return new class() Iterator {
+                    public bool hasNext() {
+                        return wrappedIterator.hasNext();
+                    }
+
+                    public Object next() {
+                        return (cast(ViewerElementWrapper) wrappedIterator.next()).unwrap();
+                    }
+
+                    public void remove() {
+                        wrappedIterator.remove();
+                    }
+                };
+            }
+
+            public bool remove(Object o) {
+                return wrappedKeySet.remove(new ViewerElementWrapper(o, comparer));
+            }
+
+            public bool removeAll(Collection c) {
+                bool changed = false;
+                for (Iterator iterator = c.iterator(); iterator.hasNext();)
+                    changed |= wrappedKeySet
+                            .remove(new ViewerElementWrapper(iterator.next(), comparer));
+                return changed;
+            }
+
+            public bool retainAll(Collection c) {
+                bool changed = false;
+                Object[] toRetain = c.toArray();
+                outer: for (Iterator iterator = iterator(); iterator.hasNext();) {
+                    Object element = iterator.next();
+                    for (int i = 0; i < toRetain.length; i++)
+                        if (comparer.equals(element, toRetain[i]))
+                            continue outer;
+                    // element not contained in collection, remove.
+                    remove(element);
+                    changed = true;
+                }
+                return changed;
+            }
+
+            public int size() {
+                return wrappedKeySet.size();
+            }
+
+            public Object[] toArray() {
+                return toArray(new Object[wrappedKeySet.size()]);
+            }
+
+            public Object[] toArray(Object[] a) {
+                int size = wrappedKeySet.size();
+                ViewerElementWrapper[] wrappedArray = cast(ViewerElementWrapper[]) wrappedKeySet
+                        .toArray(new ViewerElementWrapper[size]);
+                Object[] result = a;
+                if (a.length < size) {
+                    result = cast(Object[]) Array.newInstance(a.getClass()
+                            .getComponentType(), size);
+                }
+                for (int i = 0; i < size; i++)
+                    result[i] = wrappedArray[i].unwrap();
+                return result;
+            }
+
+            public bool equals(Object obj) {
+                if (obj is this)
+                    return true;
+                if (obj is null || !(null !is cast(Set)obj))
+                    return false;
+                Set that = cast(Set) obj;
+                return this.size() is that.size() && containsAll(that);
+            }
+
+            public int hashCode() {
+                return wrappedKeySet.hashCode();
+            }
+        };
+    }
+
+    public Object put(Object key, Object value) {
+        return wrappedMap.put(new ViewerElementWrapper(key, comparer), value);
+    }
+
+    public void putAll(Map other) {
+        for (Iterator iterator = other.entrySet().iterator(); iterator
+                .hasNext();) {
+            Map.Entry entry = cast(Map.Entry) iterator.next();
+            wrappedMap.put(new ViewerElementWrapper(entry.getKey(), comparer), entry.getValue());
+        }
+    }
+
+    public Object remove(Object key) {
+        return wrappedMap.remove(new ViewerElementWrapper(key, comparer));
+    }
+
+    public int size() {
+        return wrappedMap.size();
+    }
+
+    public Collection values() {
+        return wrappedMap.values();
+    }
+
+    public bool equals(Object obj) {
+        if (obj is this)
+            return true;
+        if (obj is null || !(null !is cast(Map)obj))
+            return false;
+        Map that = cast(Map) obj;
+        return this.entrySet().equals(that.entrySet());
+    }
+
+    public int hashCode() {
+        return wrappedMap.hashCode();
+    }
+
+    /**
+     * Returns a Map for mapping viewer elements as keys to values, using the
+     * given {@link IElementComparer} for key comparisons.
+     * 
+     * @param comparer
+     *            the element comparer to use in key comparisons. If null, the
+     *            returned map will compare keys according to the standard
+     *            contract for {@link Map} interface contract.
+     * @return a Map for mapping viewer elements as keys to values, using the
+     *         given {@link IElementComparer} for key comparisons.
+     */
+    public static Map withComparer(IElementComparer comparer) {
+        if (comparer is null)
+            return new HashMap();
+        return new ViewerElementMap(comparer);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementSet.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,205 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 215531)
+ *     Matthew Hall - bug 124684
+ ******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ViewerElementSet;
+
+import java.lang.all;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * A {@link Set} of elements in a {@link StructuredViewer}. Elements of the set
+ * are compared using an {@link IElementComparer} instead of
+ * {@link #equals(Object)}.
+ * <p>
+ * This class is <i>not</i> a strict implementation the {@link Set} interface.
+ * It intentionally violates the {@link Set} contract, which requires the use of
+ * {@link #equals(Object)} when comparing elements. This class is designed for
+ * use with {@link StructuredViewer} which uses {@link IElementComparer} for
+ * element comparisons.
+ * 
+ * @since 1.2
+ */
+public class ViewerElementSet : Set {
+    private final Set wrappedSet;
+    private final IElementComparer comparer;
+
+    /**
+     * Constructs a ViewerElementSet using the given {@link IElementComparer}.
+     * 
+     * @param comparer
+     *            the {@link IElementComparer} used for comparing elements.
+     */
+    public this(IElementComparer comparer) {
+        Assert.isNotNull(comparer);
+        this.wrappedSet = new HashSet();
+        this.comparer = comparer;
+    }
+
+    /**
+     * Constructs a ViewerElementSet containing all the elements in the
+     * specified collection.
+     * 
+     * @param collection
+     *            the collection whose elements are to be added to this set.
+     * @param comparer
+     *            the {@link IElementComparer} used for comparing elements.
+     */
+    public this(Collection collection, IElementComparer comparer) {
+        this(comparer);
+        addAll(collection);
+    }
+
+    public bool add(Object o) {
+        return wrappedSet.add(new ViewerElementWrapper(o, comparer));
+    }
+
+    public bool addAll(Collection c) {
+        bool changed = false;
+        for (Iterator iterator = c.iterator(); iterator.hasNext();)
+            changed |= wrappedSet.add(new ViewerElementWrapper(iterator.next(),
+                    comparer));
+        return changed;
+    }
+
+    public void clear() {
+        wrappedSet.clear();
+    }
+
+    public bool contains(Object o) {
+        return wrappedSet.contains(new ViewerElementWrapper(o, comparer));
+    }
+
+    public bool containsAll(Collection c) {
+        for (Iterator iterator = c.iterator(); iterator.hasNext();)
+            if (!wrappedSet.contains(new ViewerElementWrapper(iterator.next(),
+                    comparer)))
+                return false;
+        return true;
+    }
+
+    public bool isEmpty() {
+        return wrappedSet.isEmpty();
+    }
+
+    public Iterator iterator() {
+        final Iterator wrappedIterator = wrappedSet.iterator();
+        return new class() Iterator {
+            public bool hasNext() {
+                return wrappedIterator.hasNext();
+            }
+
+            public Object next() {
+                return (cast(ViewerElementWrapper) wrappedIterator.next()).unwrap();
+            }
+
+            public void remove() {
+                wrappedIterator.remove();
+            }
+        };
+    }
+
+    public bool remove(Object o) {
+        return wrappedSet.remove(new ViewerElementWrapper(o, comparer));
+    }
+
+    public bool removeAll(Collection c) {
+        bool changed = false;
+        for (Iterator iterator = c.iterator(); iterator.hasNext();)
+            changed |= remove(iterator.next());
+        return changed;
+    }
+
+    public bool retainAll(Collection c) {
+        // Have to do this the slow way to ensure correct comparisons. i.e.
+        // cannot delegate to c.contains(it) since we can't be sure will
+        // compare elements the way we want.
+        bool changed = false;
+        Object[] retainAll = c.toArray();
+        outer: for (Iterator iterator = iterator(); iterator.hasNext();) {
+            Object element = iterator.next();
+            for (int i = 0; i < retainAll.length; i++) {
+                if (comparer.equals(element, retainAll[i])) {
+                    continue outer;
+                }
+            }
+            iterator.remove();
+            changed = true;
+        }
+        return changed;
+    }
+
+    public int size() {
+        return wrappedSet.size();
+    }
+
+    public Object[] toArray() {
+        return toArray(new Object[wrappedSet.size()]);
+    }
+
+    public Object[] toArray(Object[] a) {
+        int size = wrappedSet.size();
+        ViewerElementWrapper[] wrappedArray = cast(ViewerElementWrapper[]) wrappedSet
+                .toArray(new ViewerElementWrapper[size]);
+        Object[] result = a;
+        if (a.length < size) {
+            result = cast(Object[]) Array.newInstance(a.getClass()
+                    .getComponentType(), size);
+        }
+        for (int i = 0; i < size; i++)
+            result[i] = wrappedArray[i].unwrap();
+        return result;
+    }
+
+    public bool equals(Object obj) {
+        if (obj is this)
+            return true;
+        if (!(null !is cast(Set)obj))
+            return false;
+        Set that = cast(Set) obj;
+        return size() is that.size() && containsAll(that);
+    }
+
+    public int hashCode() {
+        int hash = 0;
+        for (Iterator iterator = iterator(); iterator.hasNext();) {
+            Object element = iterator.next();
+            hash += element is null ? 0 : element.hashCode();
+        }
+        return hash;
+    }
+
+    /**
+     * Returns a Set for holding viewer elements, using the given
+     * {@link IElementComparer} for comparisons.
+     * 
+     * @param comparer
+     *            the element comparer to use in element comparisons. If null,
+     *            the returned set will compare elements according to the
+     *            standard contract for {@link Set} interface contract.
+     * @return a Set for holding viewer elements, using the given
+     *         {@link IElementComparer} for comparisons.
+     */
+    public static Set withComparer(IElementComparer comparer) {
+        if (comparer is null)
+            return new HashSet();
+        return new ViewerElementSet(comparer);
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerElementWrapper.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,58 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 215531)
+ ******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ViewerElementWrapper;
+
+import java.lang.all;
+
+import org.eclipse.jface.viewers.IElementComparer;
+
+/**
+ * A wrapper class for viewer elements, which uses an {@link IElementComparer}
+ * for computing {@link Object#equals(Object) equality} and
+ * {@link Object#hashCode() hashes}.
+ * 
+ * @since 1.2
+ */
+public class ViewerElementWrapper {
+    private final Object element;
+    private final IElementComparer comparer;
+
+    /**
+     * Constructs a ViewerElementWrapper wrapping the given element
+     * 
+     * @param element
+     *            the element being wrapped
+     * @param comparer
+     *            the comparer to use for computing equality and hash codes.
+     */
+    public this(Object element, IElementComparer comparer) {
+        if (comparer is null)
+            throw new NullPointerException();
+        this.element = element;
+        this.comparer = comparer;
+    }
+
+    public bool equals(Object obj) {
+        if (!(null !is cast(ViewerElementWrapper)obj)) {
+            return false;
+        }
+        return comparer.equals(element, (cast(ViewerElementWrapper) obj).element);
+    }
+
+    public int hashCode() {
+        return comparer.hashCode(element);
+    }
+
+    Object unwrap() {
+        return element;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerInputObservableValue.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2007 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 206839)
+ *******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ViewerInputObservableValue;
+
+import java.lang.all;
+
+import org.eclipse.core.databinding.observable.Diffs;
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.core.databinding.observable.value.AbstractObservableValue;
+import org.eclipse.jface.util.Util;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Observes the input of a <code>Viewer</code>.
+ * <p>
+ * This observer is blind to changes in the viewer's input unless its
+ * {@link #setValue(Object)} method is called directly.
+ * 
+ * @since 1.2
+ */
+public class ViewerInputObservableValue : AbstractObservableValue {
+
+  private final Viewer viewer;
+
+  /**
+   * Constructs a new instance associated with the provided <code>viewer</code>.
+   * 
+   * @param realm
+   * @param viewer
+   */
+  public this( Realm realm, Viewer viewer ) {
+    super( realm );
+    if ( viewer is null ) {
+      throw new IllegalArgumentException( "The 'viewer' parameter is null." ); //$NON-NLS-1$
+    }
+
+    this.viewer = viewer;
+  }
+
+  /**
+   * Sets the input to the provided <code>value</code>. Value change events are
+   * fired after input is set in the viewer.
+   * 
+   * @param value object to set as input
+   */
+  protected void doSetValue( Object value ) {
+    Object oldValue = doGetValue();
+    viewer.setInput( value );
+    if ( !Util.equals( oldValue, value ) ) {
+      fireValueChange( Diffs.createValueDiff( oldValue, value ) );
+    }
+  }
+
+  /**
+   * Retrieves the current input.
+   * 
+   * @return the current input
+   */
+  protected Object doGetValue() {
+    return viewer.getInput();
+  }
+
+  public Object getValueType() {
+    return null;
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerMultipleSelectionObservableList.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,49 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Brad Reynolds - bug 137877
+ *     Brad Reynolds - bug 164653
+ *     Brad Reynolds - bug 147515
+ *     Ashley Cambrell - bug 198906
+ *******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ViewerMultipleSelectionObservableList;
+
+import java.lang.all;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.viewers.IViewerObservableList;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Observes single selection of a <code>Viewer</code>.
+ * 
+ * @since 1.2
+ */
+public class ViewerMultipleSelectionObservableList :
+        SelectionProviderMultipleSelectionObservableList ,
+        IViewerObservableList {
+
+    private Viewer viewer;
+
+    /**
+     * @param realm
+     * @param viewer
+     * @param elementType
+     */
+    public this(Realm realm, Viewer viewer,
+            Object elementType) {
+        super(realm, viewer, elementType);
+        this.viewer = viewer;
+    }
+
+    public Viewer getViewer() {
+        return viewer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerSingleSelectionObservableValue.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,47 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Brad Reynolds - bug 137877
+ *     Brad Reynolds - bug 164653
+ *     Brad Reynolds - bug 147515
+ *     Ashley Cambrell - bug 198906
+ *******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ViewerSingleSelectionObservableValue;
+
+import java.lang.all;
+
+import org.eclipse.core.databinding.observable.Realm;
+import org.eclipse.jface.databinding.viewers.IViewerObservableValue;
+import org.eclipse.jface.viewers.Viewer;
+
+/**
+ * Observes single selection of a <code>Viewer</code>.
+ * 
+ * @since 1.2
+ */
+public class ViewerSingleSelectionObservableValue :
+        SelectionProviderSingleSelectionObservableValue ,
+        IViewerObservableValue {
+
+    private Viewer viewer;
+
+    /**
+     * @param realm
+     * @param viewer
+     */
+    public this(Realm realm, Viewer viewer) {
+        super(realm, viewer);
+        this.viewer = viewer;
+    }
+
+    public Viewer getViewer() {
+        return viewer;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/org.eclipse.jface.databinding/src/org/eclipse/jface/internal/databinding/viewers/ViewerUpdater.d	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Matthew Hall and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     Matthew Hall - initial API and implementation (bug 226765)
+ ******************************************************************************/
+
+module org.eclipse.jface.internal.databinding.viewers.ViewerUpdater;
+
+import java.lang.all;
+
+import org.eclipse.jface.util.Util;
+import org.eclipse.jface.viewers.IElementComparer;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.StructuredViewer;
+
+/**
+ * NON-API - An interface for updating a viewer's elements.
+ * 
+ * @since 1.2
+ */
+public abstract class ViewerUpdater {
+    private final StructuredViewer viewer;
+
+    /**
+     * Constructs a ViewerUpdater for updating the specified viewer.
+     * 
+     * @param viewer
+     *            the viewer which will be updated through this instance.
+     */
+    protected this(StructuredViewer viewer) {
+        this.viewer = viewer;
+    }
+
+    /**
+     * Insert the element into the viewer at the specified position.
+     * 
+     * @param element
+     *            the element to add
+     * @param position
+     *            the position of the element
+     */
+    public abstract void insert(Object element, int position);
+
+    /**
+     * Remove the element from the viewer
+     * 
+     * @param element
+     *            the element to remove
+     * @param position
+     *            the position of the element
+     */
+    public abstract void remove(Object element, int position);
+
+    /**
+     * Replace the specified element at the given position with the new element.
+     * 
+     * @param oldElement
+     *            the element being replaced
+     * @param newElement
+     *            the element that replaces <code>oldElement</code>
+     * @param position
+     *            the position of the element being replaced.
+     */
+    public void replace(Object oldElement, Object newElement, int position) {
+        remove(oldElement, position);
+        insert(newElement, position);
+    }
+
+    /**
+     * Moves the specified element from the specified old position to the
+     * specified new position. No action is taken if the viewer has a sorter or
+     * filter(s).
+     * 
+     * @param element
+     *            the element being moved
+     * @param oldPosition
+     *            the position of the element before it is moved
+     * @param newPosition
+     *            the position of the element after it is moved
+     */
+    public void move(Object element, int oldPosition, int newPosition) {
+        if (viewer.getComparator() is null && viewer.getFilters().length is 0) {
+            IStructuredSelection selection = cast(IStructuredSelection) viewer
+                    .getSelection();
+
+            remove(element, oldPosition);
+            insert(element, newPosition);
+
+            // Preserve selection
+            if (!selection.isEmpty()) {
+                IElementComparer comparer = viewer.getComparer();
+                Object[] selectedElements = selection.toArray();
+                for (int i = 0; i < selectedElements.length; i++) {
+                    if (comparer is null ? Util.equals(element,
+                            selectedElements[i]) : comparer.equals(element,
+                            selectedElements[i])) {
+                        viewer.setSelection(selection);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds the elements to the viewer.
+     * 
+     * @param elements
+     *            the elements to add
+     */
+    public abstract void add(Object[] elements);
+
+    /**
+     * Removes the elements from the viewer
+     * 
+     * @param elements
+     *            the elements to remove
+     */
+    public abstract void remove(Object[] elements);
+}
--- a/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.d	Sun Apr 12 12:27:13 2009 +0200
+++ b/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet44.d	Tue Apr 14 11:35:29 2009 +0200
@@ -28,6 +28,24 @@
 
 import java.lang.all;
 
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.jobs.IJobChangeListener;
+import tango.io.Stdout;
+
+class MyJob : Job {
+    this(char[] name) {
+        super(name);
+    }
+    public IStatus run(IProgressMonitor monitor) {
+        Stdout.formatln("doing job");
+        return Status.OK_STATUS;
+    }
+}
+
+
 void main (String [] args) {
     Display display = new Display ();
     Cursor cursor = new Cursor (display, SWT.CURSOR_HAND);
@@ -37,7 +55,11 @@
     b.setBounds (10, 10, 200, 200);
     b.addListener (SWT.Selection, new class() Listener{
         public void handleEvent (Event e) {
-            b.setCursor (cursor);
+            //b.setCursor (cursor);
+            auto job = new MyJob("test");
+            job.schedule();
+            job.join;
+            Stdout.formatln("job done");
         }
     });
     while (!shell.isDisposed ()) {
--- a/rakefile	Sun Apr 12 12:27:13 2009 +0200
+++ b/rakefile	Tue Apr 14 11:35:29 2009 +0200
@@ -318,6 +318,11 @@
     buildTree( "org.eclipse.core.jobs", "src", "res" )
 end
 
+desc "Build Eclipse Core Databinding"
+task :corebind do
+    buildTree( "org.eclipse.core.databinding", "src", "res" )
+end
+
 desc "Build JFace"
 task :jface do
     buildTree( "org.eclipse.jface", "src", "res" )
@@ -335,6 +340,11 @@
     buildTree( "org.eclipse.jface.text", "src", "res" )
 end
 
+desc "Build JFace.Databinding"
+task :jfacebind do
+    buildTree( "org.eclipse.jface.databinding", "src", "res" )
+end
+
 desc "Build UI Forms"
 task :uiforms do
     buildTree( "org.eclipse.ui.forms", "src", "res" )
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/refactors.txt	Tue Apr 14 11:35:29 2009 +0200
@@ -0,0 +1,22 @@
+set noignorecase
+bufdo retab
+bufdo set ff=unix
+bufdo %s:==:is:g
+bufdo %s:!=:!is:g
+
+bufdo %s:\<boolean\>:bool:g
+bufdo %s:^\(\s*\(public\|private\|protected\|\)\s*\)\([A-Z]\i*\)\(\s*(\):\1this\4:
+bufdo %s:\<implements\>:,:gc
+bufdo %s:\<implements\>:\::gc
+bufdo %s:\<extends\>:\::gc
+bufdo %s:\<new\s\+\([A-Z]\i*\)\s*\(([^)]*)\)\s*{:new class\2 \1 {:
+bufdo %s:\<[A-Z]\i*\.this\>:this.outer:g
+ gg/package
cemoduleo%€kb€kb;bhvlkJf;bhvF;xi.o
import java.lang.all;       
+bufdo %s:(\s*\(byte\|short\|int\|long\|float\|double\|[A-Z]\i*\(\.[A-Z]\i*\)\?\)\s*\(\[\s*]\s*\)\?):cast\0:g
+
+bufdo %s:\([(,|&=]\)\_s*\(\i\+\)\_s\+instanceof\_s\+\([A-Z]\i*\(\.[A-Z]\i*\)\?\s*\(\[\s*]\s*\)\?\)\(\_s*[),|&?]\):\1 null !is cast(\3)\2 \6:g
+bufdo %s:\<Class\>:ClassInfo:gc
+bufdo %s:\.class\>:.classinfo:gc
+bufdo %s:\(\<bool\>\s*\)\<equals\>\(\s*(\):override \1opEquals\2:c
+bufdo %s:\()\_s*\)throws\_s\+[A-Z]\i*\_s*\(,\_s*[A-Z]\i*\_s*\)*{:\1{:
+